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)
}
life
life