对象转换

对象转换在请求处理中非常常见。我们推荐将输入和输出定义为 struct 结构体对象,以便于结构化的参数输入输出维护。 GoFrame 框架支持非常便捷的对象转换,支持将客户端提交的参数如 Query 参数、表单参数、内容参数、 JSON/XML 等参数非常便捷地转换为指定的 struct 结构体,并且支持提交参数与 struct 属性的映射关系维护。

对象转换方法使用 Request 对象的 Parse 方法或者 Get*Struct 方法,具体方法定义请参考API文档: https://pkg.go.dev/github.com/gogf/gf/v2/net/ghttp#Request

参数映射

默认规则

客户端提交的参数如果需要映射到服务端定义的 struct 属性上,可以采用默认的映射关系,这一点非常方便。默认的转换规则如下:

  1. struct 中需要匹配的属性必须为 公开属性(首字母大写)。
  2. 参数名称会自动按照 不区分大小写忽略 -/_/空格 符号 的形式与 struct 属性进行匹配。
  3. 如果匹配成功,那么将键值赋值给属性,如果无法匹配,那么忽略该键值。

以下是几个匹配的示例:

  1. map键名 struct属性 是否匹配
  2. name Name match
  3. Email Email match
  4. nickname NickName match
  5. NICKNAME NickName match
  6. Nick-Name NickName match
  7. nick_name NickName match
  8. nick name NickName match
  9. NickName Nick_Name match
  10. Nick-name Nick_Name match
  11. nick_name Nick_Name match
  12. nick name Nick_Name match

由于底层对象转换实现使用的是 gconv 模块,因此也支持 c/gconv/json 标签,更详细的规则可以参考 类型转换-Struct转换 章节。

自定义规则

请在对象属性与参数名字差异较大的业务场景下使用自定义映射规则,否则请使用默认的参数映射规则。因为大量的自定义规则标签会增加代码的维护成本。

自定义的参数映射规则可以通过为 struct 属性绑定 tag 实现, tag 名称可以为 p/param/params。例如:

  1. type User struct{
  2. Id int
  3. Name string
  4. Pass1 string `p:"password1"`
  5. Pass2 string `p:"password2"`
  6. }

其中我们使用了 p 标签来指定该属性绑定的参数名称。 password1 参数将会映射到 Pass1 属性, password2 将会映射到 Pass2 属性上。其他属性采用默认的转换规则即可,无需设置 tag

Parse 转换

我们同时可以使用 Parse 方法来实现 struct 的转换,该方法是一个便捷方法,内部会自动进行转换及数据校验,但如果 struct 中没有校验 tag 的绑定将不会执行校验逻辑。

请求输入-对象处理 - 图1注意

GoFrame v2 版本开始,我们推荐使用结构化的方式来定义路由方法,更便捷地管理输入输出数据结构及其实例对象,具体请参考: 路由注册-规范路由

使用示例:

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/net/ghttp"
  5. )
  6. type RegisterReq struct {
  7. Name string
  8. Pass string `p:"password1"`
  9. Pass2 string `p:"password2"`
  10. }
  11. type RegisterRes struct {
  12. Code int `json:"code"`
  13. Error string `json:"error"`
  14. Data interface{} `json:"data"`
  15. }
  16. func main() {
  17. s := g.Server()
  18. s.BindHandler("/register", func(r *ghttp.Request) {
  19. var req *RegisterReq
  20. if err := r.Parse(&req); err != nil {
  21. r.Response.WriteJsonExit(RegisterRes{
  22. Code: 1,
  23. Error: err.Error(),
  24. })
  25. }
  26. // ...
  27. r.Response.WriteJsonExit(RegisterRes{
  28. Data: req,
  29. })
  30. })
  31. s.SetPort(8199)
  32. s.Run()
  33. }

在该示例中,我们定义了两个结构体: RegisterReq 用于参数接收, RegisterRes 用于数据返回。

其中,我们使用 r.Parse(&req) 将客户端提交的参数转换为 RegisterReq 对象,当转换成功时 req 变量将会被初始化赋值(默认情况下为 nil),否则该方法返回 err 并且 req 变量为 nil。返回数据结构通过 RegisterRes 定义,并且返回格式为 JSON,通过 r.Response.WriteJsonExit 实现,该方法将 RegisterRes 根据其内部定义的 json 标签转换为 JSON 格式并退出当前服务方法,不再执行该服务方法的后续逻辑。

为了演示测试效果,这里在正常的返回结果 Data 属性中将 RegisterReq 对象返回,由于该对象没有绑定 json 标签,因此返回的 JSON 字段将会为其属性名称。

执行后,我们通过 curl 工具来测试一下:

  1. $ curl "http://127.0.0.1:8199/register?name=john&password1=123&password2=456"
  2. {"code":0,"error":"","data":{"Name":"john","Pass":"123","Pass2":"456"}}
  3. $ curl -d "name=john&password1=123&password2=456" -X POST "http://127.0.0.1:8199/register"
  4. {"code":0,"error":"","data":{"Name":"john","Pass":"123","Pass2":"456"}}

我们使用了 GETPOST 两种提交方式来做测试,可以看到服务端都能完美地获取到提交参数并完成对象转换。