正确姿势是:定义带validate tag的结构体,用ctx.readform/readquery绑定数据,再显式调用validator.struct()校验;validator实例需复用,错误信息应遍历validationerrors提取字段和规则。

iris 中用 Validator 做表单校验的正确姿势
iris 本身不内置 Validator,所谓 Validator 通常指第三方库(如 go-playground/validator)配合 iris 的 ctx.ReadForm 或结构体绑定使用。直接调 ctx.ReadForm(&v) 不会自动触发校验,必须显式调用 Validate.Struct(v)。
常见错误是以为加了 struct tag 就能自动报错——其实 iris 不拦截、不包装、不自动调用 validator,全靠你自己组织逻辑。
- 先定义带
validatetag 的结构体,比如:type LoginForm struct { Username string `validate:"required,min=3,max=20"` Password string `validate:"required,min=6"` } - 在 handler 里手动绑定 + 校验:
v := validator.New() form := new(LoginForm) if err := ctx.ReadForm(form); err != nil { ctx.StatusCode(400) ctx.JSON(map[string]string{"error": "解析表单失败"}) return } if err := v.Struct(form); err != nil { ctx.StatusCode(400) ctx.JSON(map[string]string{"error": err.Error()}) return } - 注意
validator.New()应该复用(比如定义为包级变量),不要每次请求都新建,否则性能损耗明显
struct tag 里哪些校验规则 iris 能识别?
iris 本身不解析 validate tag,它只负责把表单字段映射到 struct 字段。真正起作用的是你引入的 go-playground/validator 库——iris 完全透明,tag 写法和标准 validator 一致。
常用且稳妥的规则包括:required、email、url、min/max(对字符串是长度,对数字是数值)、oneof、gt/gte 等。别写 isemail 这种旧版写法,新版统一用 email。
-
required_if、required_with这类条件校验可用,但要注意字段顺序:被依赖字段必须已赋值,否则可能跳过校验 - 自定义函数需通过
v.RegisterValidation注册,且函数签名必须是func(fl validator.FieldLevel) bool - 嵌套 struct 默认不递归校验,要加
divetag,例如:Profile *UserProfile `validate:"dive"`
为什么 ctx.ReadQuery 或 ctx.URLParam 不走 validator?
因为 ReadQuery 和 URLParam 返回的是原始字符串或基本类型,没有 struct 上下文,validator 没有载体可绑。想校验 URL 参数,得先转成 struct 再校验,或者手写 if 判断。
推荐做法:把 query 参数也定义进同一个 form struct,用 ctx.ReadQuery 绑定(它支持 struct tag 映射),再统一走 v.Struct:
type SearchForm struct {
Q string `form:"q" validate:"required,min=1,max=100"`
Page int `form:"page" validate:"omitempty,gt=0,lte=1000"`
}
// ...
form := new(SearchForm)
if err := ctx.ReadQuery(form); err != nil { ... }
if err := v.Struct(form); err != nil { ... }
-
ReadQuery和ReadForm都支持formtag,优先级高于字段名,默认用字段名映射,但建议显式写form:"xxx"避免歧义 -
URLParam是路径参数(如/user/:id),类型固定为 string,iris 不做类型转换,也不支持 tag 校验,必须自己strconv.Atoi后再判断范围
校验失败时怎么返回友好的错误信息?
err.Error() 返回的是整段英文描述(如 Key: 'LoginForm.Username' Error:Field validation for 'Username' failed on the 'required' tag),前端很难解析。应该遍历 err.(validator.ValidationErrors) 提取字段名和规则名。
if err := v.Struct(form); err != nil {
errs := err.(validator.ValidationErrors)
errors := make(map[string]string)
for _, e := range errs {
field := e.Field() // "Username"
rule := e.Tag() // "required"
switch rule {
case "required":
errors[field] = "此项必填"
case "min":
errors[field] = "长度不能少于 " + e.Param() + " 个字符"
}
}
ctx.JSON(map[string]interface{}{"errors": errors})
}
- 务必做类型断言,
err可能是其他错误(比如 JSON 解析失败),不是所有 err 都能转成ValidationErrors -
e.Param()返回 tag 里的参数值(如min=6的"6"),比硬编码更灵活 - 如果项目多语言,建议把提示文本存在 map 里按
field + rule查,而不是写一堆 switch
validator 的核心就三步:定义结构体、绑定数据、显式校验。iris 不插手校验逻辑,也不封装 validator,这点容易被文档误导。最常漏掉的是忘记调 v.Struct,或者把 validator 实例放在 handler 里导致反复初始化。











