revel 框架修改
无    2014-03-01 16:12:13    2229    0    0
life

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

上一篇: golang 正则

下一篇: 关闭golang的 variable declared but not used 和 package imported but not used

2229 人读过