requestBody 注入
ajax发送json数据到controller, revel自动解析成相应struct( 类似SpringMvc )
controller.go, 在Controller添加RequestBody属性
type Controller struct { Name string // The controller name, e.g. "Application" Type *ControllerType // A description of the controller type. MethodName string // The method name, e.g. "Index" MethodType *MethodType // A description of the invoked action type. AppController interface{} // The controller that was instantiated. Action string // The fully qualified action name, e.g. "App.Index" Request *Request Response *Response Result Result Flash Flash // User cookie, cleared after 1 request. Session Session // Session, stored in cookie, signed. Params *Params // Parameters from URL and form (including multipart). // life RequestBody []byte // requestBody Json life Router *RouteMatch // 路由信息, 有controller, action待信息 Args map[string]interface{} // Per-request scratch space. RenderArgs map[string]interface{} // Args passed to the template. Validation *Validation // Data validation helpers }
params.go 中得到判断请求类型是否是application/json, 得到requestBody数据
func ParseParams(c *Controller, params *Params, req *Request) { params.Query = req.URL.Query() TRACE.Println("Request URL : " + req.URL.RequestURI()) TRACE.Println("Request ContentType : " + req.ContentType) // Parse the body depending on the content type. switch req.ContentType { case "application/x-www-form-urlencoded": // Typical form. if err := req.ParseForm(); err != nil { WARN.Println("Error parsing request body:", err) } else { params.Form = req.Form } case "multipart/form-data": // Multipart form. // TODO: Extract the multipart form param so app can set it. if err := req.ParseMultipartForm(32 << 20 /* 32 MB */); err != nil { WARN.Println("Error parsing request body:", err) } else { params.Form = req.MultipartForm.Value params.Files = req.MultipartForm.File } // life // json 读取所有到c.RequestBody里 case "application/json": body, _ := ioutil.ReadAll(c.Request.Body) c.RequestBody = body TRACE.Println("RequestBody : " + string(body)) } params.Values = params.calcValues() }
binder.go中添加bindJson()方法
// life func BindJson(requestBody []byte, typ reflect.Type) reflect.Value { // defer func() { // if err := recover(); err != nil { // ERROR.Println("BindJson ERROR!!!") // panic(err) // } // }() // 实例化该struct result := reflect.New(typ) // non-pointer // 转成iterface才能unmarshal, 不然虽然没有错误, 但不能设值 i := result.Interface() err := json.Unmarshal(requestBody, i); if err != nil { // 用ERROR.fatal()会终止程序 ERROR.Println(string(requestBody)) ERROR.Println("JSON Unmarshal error!!") ERROR.Println(result) ERROR.Println(err) } TRACE.Println("UnMarshal requestBody:" + string(requestBody)) TRACE.Println(result) TRACE.Println(i) return result.Elem() // 返回Notebook, 不是指针, 如果result则是*Notebook }
invoker.go中, controller中第一个参数为接收application/json的参数.
func ActionInvoker(c *Controller, _ []Filter) { // Instantiate the method. methodValue := reflect.ValueOf(c.AppController).MethodByName(c.MethodType.Name) // Collect the values for the method's arguments. var methodArgs []reflect.Value // 这里要先判断RequestBody是否有值, 如果有值, 那么bind的第一个参数则为该值!!! var boundArg reflect.Value start := 0 if len(c.MethodType.Args) > 0 && c.RequestBody != nil { boundArg = BindJson(c.RequestBody, c.MethodType.Args[0].Type) // binder.go methodArgs = append(methodArgs, boundArg) start = 1 } for _, arg := range c.MethodType.Args[start:] { // If they accept a websocket connection, treat that arg specially. if arg.Type == websocketType { boundArg = reflect.ValueOf(c.Request.Websocket) } else { TRACE.Println("Binding:", arg.Name, "as", arg.Type) boundArg = Bind(c.Params, arg.Name, arg.Type) // binder.go } methodArgs = append(methodArgs, boundArg) } var resultValue reflect.Value if methodValue.Type().IsVariadic() { resultValue = methodValue.CallSlice(methodArgs)[0] } else { resultValue = methodValue.Call(methodArgs)[0] } if resultValue.Kind() == reflect.Interface && !resultValue.IsNil() { c.Result = resultValue.Interface().(Result) } }
生产环境 500 错误会打印出statck
panic.go. 具体应该在errors.go中的 NewErrorFromPanic 方法中 (该方法返回为nil)
func handleInvocationPanic(c *Controller, err interface{}) { error := NewErrorFromPanic(err) if error == nil { // 不能这样啊! // 这样导致会在浏览器输出错误信息! ERROR.Print(err, "\n", string(debug.Stack())) /* c.Response.Out.WriteHeader(500) c.Response.Out.Write(debug.Stack()) */ // life you can c.Result = c.RenderError(&Error{}) return } ERROR.Print(err, "\n", error.Stack) c.Result = c.RenderError(error) }