validator.v10通过结构体字段的validate标签实现字段级校验,不依赖框架,需导出字段、手动调用struct触发;须区分binding(gin解析+校验)与validate(纯校验),校验前需用conform清洗字符串,标准流程为parse→conform→validate→业务校验。

用 validator.v10 做结构体字段级校验
直接在结构体字段上加 validate 标签是最常用、最可控的校验方式。它不依赖框架,但和 go-playground/validator/v10 配合使用时能自动触发规则检查。
常见错误是把 binding 和 validate 混用——Gin 的 binding 是框架层解析+校验一体化行为,而纯 validator 库只负责校验已赋值的结构体字段,不参与解析过程。
- 安装:
go get github.com/go-playground/validator/v10 - 定义结构体时注意字段必须是导出的(首字母大写),否则
validator无法访问:type LoginForm struct { Username string `validate:"required,min=3,max=32"` } - 手动触发校验:
validate := validator.New(); err := validate.Struct(form),返回的err是validator.ValidationErrors类型,可遍历获取具体字段和规则 - 避免在
Validate()方法里做副作用操作(如查库、发 HTTP 请求),校验逻辑应保持无状态
用 conform 清洗字符串输入(去空格、转小写、标准化邮箱)
校验前清洗比校验后修复更安全。比如用户提交 " TEST@EXAMPLE.COM ",直接校验邮箱格式会失败;但如果先用 conform 统一处理,就能让规则更聚焦语义而非格式噪声。
conform 不做校验,只做清洗,和 validator 是互补关系。它通过结构体标签声明清洗策略,调用 conform.Strings(&v) 即生效。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 安装:
go get github.com/leebenson/conform - 支持的标签有:
conform:"email"(转小写+去两端空格)、conform:"trim"、conform:"lower"、conform:"upper"等 - 注意:它只处理
string字段,对*string或嵌套结构体无效;清洗必须在ParseForm或绑定之后、校验之前执行 - 不要依赖它防 XSS——
conform不过滤 HTML 标签,输出到模板时仍需用html/template自动转义
手动校验 + 类型转换时绕不开的坑
不是所有字段都适合塞进结构体标签。比如动态表单、多选数组、带单位的数字("128MB")、需要查库唯一性判断的用户名,就得手动处理。这时最容易翻车的是类型转换和边界判断。
典型现象:strconv.Atoi(r.FormValue("age")) 返回 0, nil,你以为是整数 0,其实是空字符串转出来的假成功;或者前端传了 "1e5" 这种科学计数法,Atoi 直接报错但你没 catch。
- 永远先检查
len(r.FormValue("field")) > 0,再做转换,别信Atoi的nil错误就等于“是数字” - 数字类字段优先用
strconv.ParseInt(input, 10, 64),它比Atoi更明确,且能区分"0"和空 - 邮箱/手机号等需查库唯一性,必须放在校验流程的最后一步(即结构体字段通过
validator后),否则可能因并发导致两次校验都通过但实际插入冲突 - 密码强度这类自定义规则,别硬塞进
validate标签,单独写函数并返回标准error,和主校验流程并列调用
清洗与校验顺序不能颠倒
清洗必须在校验之前完成,否则规则会失效;但清洗本身不能替代校验——清洗解决“怎么存”,校验解决“能不能存”。这是最容易被忽略的执行时序问题。
比如一个字段要求“非空且为邮箱”,如果你先校验再清洗," a@b.c " 会被判为空;如果你清洗后不校验,"<script>alert(1)</script>" 被 conform trim 后还是恶意内容。
- 标准流程应为:
ParseForm → conform.Strings(&form) → validator.Struct(&form) → 自定义业务校验(如唯一性、权限) - 所有清洗操作必须作用于原始输入副本,别污染原始请求数据,方便审计或重试
- 如果用了 Gin,
c.ShouldBind默认不触发conform,必须显式在绑定后调用conform.Strings,否则清洗逻辑被跳过 - 日志中记录清洗前后的值(如调试期),但生产环境避免打敏感字段(密码、token)
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










