Request对象支持非常完美的请求校验能力,通过给结构体属性绑定v标签即可。由于底层校验功能通过gvalid模块实现,更详细的校验规则和介绍请参考 数据校验-结构体校验 章节。

需要注意的是,从goframe v1.16版本开始,如果参数采用结构化的输入输出管理,HTTP请求数据校验不再受结构体默认值的影响,底层调用的是gvalid组件的CheckStructWithData方法,即直接使用请求的参数执行数据校验,而给定的结构体对象仅用于校验规则和错误提示信息的定义管理。

示例1,基本使用

我们将之前的示例做下调整,增加v校验标签。

  1. package main
  2. import (
  3. "github.com/gogf/gf/frame/g"
  4. "github.com/gogf/gf/net/ghttp"
  5. )
  6. // 注册请求数据结构
  7. type RegisterReq struct {
  8. Name string `p:"username" v:"required|length:6,30#请输入账号|账号长度为:min到:max位"`
  9. Pass string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"`
  10. Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|密码长度不够|两次密码不一致"`
  11. }
  12. // 注册返回数据结构
  13. type RegisterRes struct {
  14. Code int `json:"code"`
  15. Error string `json:"error"`
  16. Data interface{} `json:"data"`
  17. }
  18. func main() {
  19. s := g.Server()
  20. s.Group("/", func(group *ghttp.RouterGroup) {
  21. group.ALL("/register", func(r *ghttp.Request) {
  22. var req *RegisterReq
  23. if err := r.Parse(&req); err != nil {
  24. r.Response.WriteJsonExit(RegisterRes{
  25. Code: 1,
  26. Error: err.Error(),
  27. })
  28. }
  29. // ...
  30. r.Response.WriteJsonExit(RegisterRes{
  31. Data: req,
  32. })
  33. })
  34. })
  35. s.SetPort(8199)
  36. s.Run()
  37. }

在该示例中,我们定义了两个结构体:RegisterReq用于参数接收,RegisterRes用于数据返回。由于该接口返回的是JSON数据结构,可以看到,只有返回的结构体中存在json标签,而接收的结构体中只有p标签。因为RegisterReq仅用于参数接收,无需设置返回的json标签。

p标签是可选的,默认情况下会通过 忽略特殊字符+不区分大小写 的规则进行属性名称匹配转换,默认匹配规则满足绝大部分业务场景。

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

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

  1. $ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=123456"
  2. {"code":0,"error":"","data":{"Name":"john","Pass":"123456","Pass2":"123456"}}
  3. $ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=12345"
  4. {"code":1,"error":"密码长度不够; 两次密码不一致","data":null}
  5. $ curl "http://127.0.0.1:8199/register"
  6. {"code":1,"error":"请输入账号; 账号长度为6到30位; 请输入密码; 密码长度不够; 请确认密码; 两次密码不一致","data":null}

示例2,校验错误处理

可以看到在以上示例中,当请求校验错误时,所有校验失败的错误都返回了,这样对于用户体验不是特别友好。当产生错误时,我们可以将校验错误转换为*gvalid.Error对象,随后可以通过灵活的方法控制错误的返回。

  1. package main
  2. import (
  3. "github.com/gogf/gf/frame/g"
  4. "github.com/gogf/gf/net/ghttp"
  5. "github.com/gogf/gf/util/gvalid"
  6. )
  7. type RegisterReq struct {
  8. Name string `p:"username" v:"required|length:6,30#请输入账号|账号长度为:min到:max位"`
  9. Pass string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"`
  10. Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|密码长度不够|两次密码不一致"`
  11. }
  12. type RegisterRes struct {
  13. Code int `json:"code"`
  14. Error string `json:"error"`
  15. Data interface{} `json:"data"`
  16. }
  17. func main() {
  18. s := g.Server()
  19. s.Group("/", func(group *ghttp.RouterGroup) {
  20. group.ALL("/register", func(r *ghttp.Request) {
  21. var req *RegisterReq
  22. if err := r.Parse(&req); err != nil {
  23. // Validation error.
  24. if v, ok := err.(gvalid.Error); ok {
  25. r.Response.WriteJsonExit(RegisterRes{
  26. Code: 1,
  27. Error: v.FirstString(),
  28. })
  29. }
  30. // Other error.
  31. r.Response.WriteJsonExit(RegisterRes{
  32. Code: 1,
  33. Error: err.Error(),
  34. })
  35. }
  36. // ...
  37. r.Response.WriteJsonExit(RegisterRes{
  38. Data: req,
  39. })
  40. })
  41. })
  42. s.SetPort(8199)
  43. s.Run()
  44. }

可以看到,当错误产生后,我们可以通过err.(gvalid.Error)断言的方式判断错误是否为校验错误,如果是的话则返回第一条校验错误,而不是所有都返回。更详细的错误控制方法,请参考 数据校验-校验结果 章节。

此外,我们这里也可以使用gerror.Current获取第一条报错信息,而不是使用断言判断。例如:

  1. var req *RegisterReq
  2. if err := r.Parse(&req); err != nil {
  3. r.Response.WriteJsonExit(RegisterRes{
  4. Code: 1,
  5. Error: gerror.Current(err).Error(),
  6. })
  7. }

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

  1. $ curl "http://127.0.0.1:8199/register"
  2. {"code":1,"error":"请输入账号","data":null}
  3. $ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=12345"
  4. {"code":1,"error":"两次密码不一致","data":null}

Content Menu