首先编写一个表单参数验证器
表单参数验证器,ginskeleton 项目骨架中的位置:app\http\validator(web|api)\(XXX业务模块)。
我们以 用户注册模块为例,完整代码路径:app/http/validator/web/users/register.go
预览在线代码
// 给出一些最常用的验证规则:
//required 必填;
//len=11 长度=11;
//min=3 如果是数字,验证的是数据范围,最小值为3,如果是文本,验证的是最小长度为3,
//max=6 如果是数字,验证的是数字最大值为6,如果是文本,验证的是最大长度为6
// mail 验证邮箱
//gt=3 对于文本就是长度>=3
//lt=6 对于文本就是长度<=6
// eqfield 等于另一个字段,常用于注册账号时,password 字段和 re_password 字段一致性校验
type Register struct {
BaseField
// 表单参数验证结构体支持匿名结构体嵌套、以及匿名结构体与普通字段组合
Phone string `form:"phone" json:"phone"` // 手机号, 非必填
CardNo string `form:"card_no" json:"card_no"` //身份证号码,非必填
Pass string `form:"pass" json:"pass" binding:"required,min=6,max=20"` // 密码为 必填,长度>=6 并且 <=20
RePass string `form:"re_pass" json:"re_pass" binding:"required,eqfield=Pass"` // 重复输入确认密码,该字段等于 Pass 字段
}
// 特别注意: 表单参数验证器结构体的函数,绝对不能绑定在指针上
// 我们这部分代码项目启动后会加载到容器,如果绑定在指针,一次请求之后,会造成容器中的代码段被污染
func (r Register) CheckParams(context *gin.Context) {
//1.先按照验证器提供的基本语法,基本可以校验90%以上的不合格参数
if err := context.ShouldBind(&r); err != nil {
// 如果验证不通过,则由错误翻译器自动翻译不合格参数提示
response.ValidatorError(context, err)
return
}
//2.继续验证具有中国特色的参数,例如 身份证号码等,基本语法校验了长度18位,
// 然后可以自行编写正则表达式等更进一步验证每一部分组成
// r.CardNo 获取值继续校验,这里省略.....
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文
// 便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(r, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "UserRegister表单验证器json化失败", "")
} else {
// 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性
(&web.Users{}).Register(extraAddBindDataContext)
}
}
再次强调一下,表单参数验证器必须实现如下函数,接收器** r Register**
不能是指针类型 ** r *Register**
,否则使用过后会污染容器中的代码。
// r Register 绝对不能使用 r *Register
// 同时我们也发现该函数形式必须符合 gin 的回调函数形式 : func (c *gin.Context) {}
// 这样才能作为gin 的回调函数传递给gin
func (r Register) CheckParams(context *gin.Context) {
}
由于在go 语言中数字被划分的很细致:int 、int8、uint8、int16、uint16、int32、uint32、int64、uint64,
如果表单参数验证器定义的数据类型很细致,后续获取时往往需要查看原始接受类型,为了简单化处理数字, 不管开发者定义的类型是什么,我们绑定在上下文时统一转为 float64 绑定在了上下文,那么开发者在后续从 Contetx 获取相关键时,如果是数字类型请统一使用 context.GetFloat64(consts.ValidatorPrefix + "age")
获取, 然后根据具体需要类型快速转换一下就行,例如 int() 函数转为 int 类型,int64() 函数转为 int64类型。
表单参数验证器中有一个函数就是自动将结构体的成员绑定在了gin的上下文Context 上:
// 该函数主要是将本结构体的字段(成员)按照 :
// consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文
// 便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(r, consts.ValidatorPrefix, context)
其次将表单参数验证器注册在容器
具体代码位置:app/http/validator/common/register_validator/web_register_validator.go
// 按照 key => value 的形式将表单参数验证器注册在容器
key = consts.ValidatorPrefix + "UsersRegister"
containers.Set(key, users.Register{})
最后在定义的路由中调用即可
// 看到这里,才发现原来一开始看见的第二个奇怪参数也只不过是一个 func (c *gin.Context) {}
// 其实gin的回调函数形式表现可以有很多种,但是本质绝对是一致的。
users.POST("register", validatorFactory.Create(consts.ValidatorPrefix+"UsersRegister"))
本章小结:
表单参数验证器为什么要注册容器?
1.从容器获取结构体效率高。
2.如果控制器按照业务模块进行划分的话,一个业务模块就是一个包,在路由文件引入时,import 部分会有很多导入的包,显得头部很臃肿,直接从容器解析,imprt 部分只有一行代码。