要在gin中实现手机号等字段自动校验,必须正确书写binding标签、在gin.default()后立即注册自定义验证器且名称严格一致,并通过中文翻译包将错误提示本地化,否则校验将失效。

要在 Gin 中让接口自动拒绝非法手机号、身份证号或密码强度不达标的请求,必须在结构体 binding 标签里写对规则,并提前注册对应验证器——写错标签名、注册时机不对、重复注册,都会导致校验完全失效。
基础 binding 标签怎么写
先定义结构体字段,用 binding 标签声明规则,多个规则用英文逗号分隔,逗号前后不能有空格。
例如:Name string `json:"name" binding:"required,min=2,max=10"` 表示 name 字段必填、长度 2~10。
常见内置规则:required(非零值)、email(邮箱格式)、min/max(数值或字符串长度)、gte/lte(大于等于/小于等于)、eqfield(字段值相等)、oneof(枚举值)。
注意:binding 标签只在调用 c.ShouldBindJSON、c.ShouldBindQuery 等方法时触发,手动赋值不会校验。
注册自定义验证器的正确位置
必须在 gin.Default() 或 gin.New() 之后、任何路由注册和 HTTP 启动之前完成注册——放错位置会导致验证器根本没被加载。
方法一:main 函数开头立即注册
第一步:r := gin.Default()
第二步:v, ok := binding.Validator.Engine().(*validator.Validate),检查是否获取到 validator 实例;
第三步:如果 ok 为 true,调用 v.RegisterValidation("chinese_mobile", mobileValidator) 注册,其中 mobileValidator 是签名 func(fl validator.FieldLevel) bool 的函数;
【注册名称必须与 binding 标签中使用的完全一致,大小写敏感】,比如标签写 binding:"chinese_mobile",就不能注册成 "ChineseMobile"。
方法二:用 if 防御重复注册
注册前加判断:if !v.HasRegisteredNamespace("chinese_mobile") { v.RegisterValidation(...) },否则同一名称注册两次会 panic。
手机号校验规则实现
先写验证函数:
func mobileValidator(fl validator.FieldLevel) bool {
s := fl.Field().String()
return regexp.MustCompile(`^1[3-9]\d{9}$`).MatchString(s)
}
这个正则匹配 11 位手机号,以 1 开头、第二位是 3–9、后面跟 9 位数字。别用 ^1\d{10}$,它会误判 10 开头的无效号。
注册后,在结构体中使用:Phone string `json:"phone" binding:"required,chinese_mobile"`。
注意:若字段类型是 *string,fl.Field().String() 会 panic,需先判空:if fl.Field().Kind() == reflect.String { ... }。
错误信息转中文提示
默认错误是英文,如 Field validation for 'Phone' failed on the 'chinese_mobile' tag,用户看不懂。
引入 github.com/go-playground/validator/v10/translations/zh 包;
调用 zh_translations.RegisterDefaultTranslations(v, trans),其中 trans 是中文翻译器实例;
最后在 ShouldBind 失败时,用 err.(validator.ValidationErrors).Translate(trans) 获取中文错误文本。
这一步不做,前端收到的永远是原始英文字段路径和 tag 名,没法直接展示给用户。
避免校验被绕过的坑
struct 字段若为指针类型(如 *string),required 只判断指针是否为 nil,不判断解引用后内容是否为空——想校验实际字符串非空,得用 required,ne= 或自定义逻辑。
binding 标签中的 - 表示跳过该字段校验,| 表示“或”关系,比如 binding:"email|url" 表示字段值是邮箱或 URL 任一即可。
如果用了 omitempty 标签,且字段值为零值(如空字符串、0、nil),Gin 默认不绑定该字段,required 就不会触发——这不是 bug,是设计行为。











