首先编写一个表单参数验证器

表单参数验证器,ginskeleton 项目骨架中的位置:app\http\validator(web|api)\(XXX业务模块)。

我们以 用户注册模块为例,完整代码路径:app/http/validator/web/users/register.go
预览在线代码

  1. // 给出一些最常用的验证规则:
  2. //required 必填;
  3. //len=11 长度=11;
  4. //min=3 如果是数字,验证的是数据范围,最小值为3,如果是文本,验证的是最小长度为3,
  5. //max=6 如果是数字,验证的是数字最大值为6,如果是文本,验证的是最大长度为6
  6. // mail 验证邮箱
  7. //gt=3 对于文本就是长度>=3
  8. //lt=6 对于文本就是长度<=6
  9. // eqfield 等于另一个字段,常用于注册账号时,password 字段和 re_password 字段一致性校验
  10. type Register struct {
  11. BaseField
  12. // 表单参数验证结构体支持匿名结构体嵌套、以及匿名结构体与普通字段组合
  13. Phone string `form:"phone" json:"phone"` // 手机号, 非必填
  14. CardNo string `form:"card_no" json:"card_no"` //身份证号码,非必填
  15. Pass string `form:"pass" json:"pass" binding:"required,min=6,max=20"` // 密码为 必填,长度>=6 并且 <=20
  16. RePass string `form:"re_pass" json:"re_pass" binding:"required,eqfield=Pass"` // 重复输入确认密码,该字段等于 Pass 字段
  17. }
  18. // 特别注意: 表单参数验证器结构体的函数,绝对不能绑定在指针上
  19. // 我们这部分代码项目启动后会加载到容器,如果绑定在指针,一次请求之后,会造成容器中的代码段被污染
  20. func (r Register) CheckParams(context *gin.Context) {
  21. //1.先按照验证器提供的基本语法,基本可以校验90%以上的不合格参数
  22. if err := context.ShouldBind(&r); err != nil {
  23. // 如果验证不通过,则由错误翻译器自动翻译不合格参数提示
  24. response.ValidatorError(context, err)
  25. return
  26. }
  27. //2.继续验证具有中国特色的参数,例如 身份证号码等,基本语法校验了长度18位,
  28. // 然后可以自行编写正则表达式等更进一步验证每一部分组成
  29. // r.CardNo 获取值继续校验,这里省略.....
  30. // 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文
  31. // 便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
  32. extraAddBindDataContext := data_transfer.DataAddContext(r, consts.ValidatorPrefix, context)
  33. if extraAddBindDataContext == nil {
  34. response.ErrorSystem(context, "UserRegister表单验证器json化失败", "")
  35. } else {
  36. // 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
  37. (&web.Users{}).Register(extraAddBindDataContext)
  38. }
  39. }

再次强调一下,表单参数验证器必须实现如下函数,接收器** r Register** 不能是指针类型 ** r *Register** ,否则使用过后会污染容器中的代码。

  1. // r Register 绝对不能使用 r *Register
  2. // 同时我们也发现该函数形式必须符合 gin 的回调函数形式 : func (c *gin.Context) {}
  3. // 这样才能作为gin 的回调函数传递给gin
  4. func (r Register) CheckParams(context *gin.Context) {
  5. }

由于在go 语言中数字被划分的很细致:int 、int8、uint8、int16、uint16、int32、uint32、int64、uint64,
如果表单参数验证器定义的数据类型很细致,后续获取时往往需要查看原始接受类型,为了简单化处理数字, 不管开发者定义的类型是什么,我们绑定在上下文时统一转为 float64 绑定在了上下文,那么开发者在后续从 Contetx 获取相关键时,如果是数字类型请统一使用 context.GetFloat64(consts.ValidatorPrefix + "age")获取, 然后根据具体需要类型快速转换一下就行,例如 int() 函数转为 int 类型,int64() 函数转为 int64类型。
表单参数验证器中有一个函数就是自动将结构体的成员绑定在了gin的上下文Context 上:

  1. // 该函数主要是将本结构体的字段(成员)按照 :
  2. // consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文
  3. // 便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
  4. extraAddBindDataContext := data_transfer.DataAddContext(r, consts.ValidatorPrefix, context)

其次将表单参数验证器注册在容器

具体代码位置:app/http/validator/common/register_validator/web_register_validator.go

  1. // 按照 key => value 的形式将表单参数验证器注册在容器
  2. key = consts.ValidatorPrefix + "UsersRegister"
  3. containers.Set(key, users.Register{})

最后在定义的路由中调用即可

  1. // 看到这里,才发现原来一开始看见的第二个奇怪参数也只不过是一个 func (c *gin.Context) {}
  2. // 其实gin的回调函数形式表现可以有很多种,但是本质绝对是一致的。
  3. users.POST("register", validatorFactory.Create(consts.ValidatorPrefix+"UsersRegister"))

本章小结:
表单参数验证器为什么要注册容器?
1.从容器获取结构体效率高。
2.如果控制器按照业务模块进行划分的话,一个业务模块就是一个包,在路由文件引入时,import 部分会有很多导入的包,显得头部很臃肿,直接从容器解析,imprt 部分只有一行代码。