使用ajax的风格,网页客户端使用json和服务器通讯,需要revel解析json格式的参数。在网上搜索,发现了一个很好的文章:http://leanote.com/blog/view/52da6ea9a3cf232efb000000
但是使用起来有两个顾虑:
1 文章里面使用的方式是直接更改revel的框架代码,以后revel升级之后需要merge。
2 在解析json的时候只将第一个参数解析,然后传给controller的方法,感觉不是很明白。
为了自己项目的需要,自己稍微修改了一下代码,可以达到下面这样的效果:
客户端发送数据按照标准的json格式:
{
"param1":"hello",
"param2": {"A":1, "B": "BBBB"}
}
服务器端,controllers/app.go的代码:
type TestStruct struct {
A int
B string
}
type App struct {
GorpController
}
func (self *App) Start() {
}
func (self *App) Index(param1 string, param2 TestStruct) revel.Result {
self.RetMap["param"] = param1
self.RetMap["who"] = "me"
self.RetMap["struct"] = param2
return self.RetOk()
}
也就是Index方法里面有两个参数,一个string类型,一个是自定义的结构。不需要额外的处理就能获取参数。
修改方式是使用一个自定义的ActionInvoker:
func decodeJson(val interface{}, argType reflect.Type) (reflect.Value, bool) {
var zero reflect.Value
switch argType.Kind() {
case reflect.Struct:
valMap, ok := val.(map[string]interface{})
if !ok {
revel.ERROR.Println("param is not a struct:", val)
return zero, false
}
ptr := reflect.New(argType)
err := mapstructure.Decode(valMap, ptr.Interface())
if nil != err {
revel.ERROR.Println("map to structure error:", err)
}
return ptr.Elem(), true
case reflect.Slice:
valSlice, ok := val.([]interface{})
if !ok {
revel.ERROR.Println("param is not a slice:", val)
return zero, false
}
retSlice := reflect.MakeSlice(argType, 0, 32)
for _, v := range valSlice {
item, ok := decodeJson(v, argType.Elem())
if !ok {
revel.ERROR.Println("slice item type error:", item)
return zero, false
}
retSlice = reflect.Append(retSlice, item)
}
return retSlice, true
default:
return reflect.ValueOf(parser.BeSimpleType(val, argType.Kind())), true
}
return zero, false
}
func MyActionInvoker(c *revel.Controller, _ []revel.Filter) {
// Instantiate the method.
methodValue := reflect.ValueOf(c.AppController).MethodByName(c.MethodType.Name)
var jsonMap map[string]interface{}
beJson := false
if strings.Contains(c.Request.ContentType, "application/json") {
decoder := json.NewDecoder(c.Request.Body)
err := decoder.Decode(&jsonMap)
if nil != err {
revel.ERROR.Println("json unmarshal error:", err)
return
}
revel.INFO.Println("body:", jsonMap)
beJson = true
}
// Collect the values for the method's arguments.
var methodArgs []reflect.Value
for _, arg := range c.MethodType.Args {
// If they accept a websocket connection, treat that arg specially.
var boundArg reflect.Value
if beJson {
item := jsonMap[arg.Name]
ret, ok := decodeJson(item, arg.Type)
if !ok {
continue
}
boundArg = ret
} else {
if arg.Type == websocketType {
boundArg = reflect.ValueOf(c.Request.Websocket)
} else {
boundArg = revel.Bind(c.Params, arg.Name, arg.Type)
}
}
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().(revel.Result)
}
}
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">实际上是原有的ActionInvoker修改了一下,在解析参数前判断content type是不是json格式的,如果是,则将json解析为map,然后分别判断map里面的字段,如果是结构体,使用一个开源的库mapstructure,将其解析到structure里面,如果是数组,则循环解析数组里面的内容,如果是string或者bool,则直接简单转换,如果是数字,则转换成float64之后在强制转换成其他数字类型。</span>
然后,在init.go里面,使用新建的这个函数替换原有函数:
func init() {
// Filters is the default set of global filters.
revel.Filters = []revel.Filter{
revel.PanicFilter, // Recover from panics and display an error page instead.
revel.RouterFilter, // Use the routing table to select the right Action
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params.
revel.SessionFilter, // Restore and write the session cookie.
revel.FlashFilter, // Restore and write the flash cookie.
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
revel.I18nFilter, // Resolve the requested language
HeaderFilter, // Add some security based headers
revel.InterceptorFilter, // Run interceptors around the action.
revel.CompressFilter, // Compress the result.
// revel.ActionInvoker, // Invoke the action.
plugins.MyActionInvoker, // Invoke the action.
}
// register startup functions with OnAppStart
// ( order dependent )
// revel.OnAppStart(InitDB)
// revel.OnAppStart(FillCache)
}