Go 结构体校验实战:用 validator 把参数合法性收拢到一处
test12026-10-051 次阅读
为什么不用手写 if 判断
接收外部请求时,参数校验最容易散落成一堆 if err != nil。go-playground/validator 用结构体 tag 统一描述规则,校验失败时一次性返回所有错误,配合自定义规则可以覆盖业务特有的约束(如密码强度、手机号格式),把"数据合法"这道关从业务代码里彻底抽离。
基础校验与错误收集
下面定义一个创建用户的入参,required、min、max、email、gte/lte 都是内置规则。校验失败时把错误转成字段级的 map,前端能精准提示。
package main
import (
"encoding/json"
"fmt"
"net/http"
"unicode"
"github.com/go-playground/validator/v10"
)
type CreateUser struct {
Name string `json:"name" validate:"required,min=2,max=20"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"gte=0,lte=150"`
Password string `json:"password" validate:"required,strongpwd"`
}
var validate = validator.New()
func handle(w http.ResponseWriter, r *http.Request) {
var req CreateUser
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "bad json", http.StatusBadRequest)
return
}
if err := validate.Struct(req); err != nil {
verrs := make(map[string]string)
for _, e := range err.(validator.ValidationErrors) {
verrs[e.Field()] = fmt.Sprintf("字段 %s 不满足规则 %s", e.Field(), e.Tag())
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnprocessableEntity)
json.NewEncoder(w).Encode(verrs)
return
}
w.WriteHeader(http.StatusCreated)
}
注册自定义规则
内置规则覆盖不了密码强度这类业务约束时,用 RegisterValidation 注册一个函数即可。注意函数签名是 validator.FieldLevel。
func strongpwd(fl validator.FieldLevel) bool {
pwd := fl.Field().String()
var hasUpper, hasNum bool
for _, r := range pwd {
if unicode.IsUpper(r) {
hasUpper = true
}
if unicode.IsDigit(r) {
hasNum = true
}
}
return len(pwd) >= 8 && hasUpper && hasNum
}
func init() {
validate.RegisterValidation("strongpwd", strongpwd)
}
跨字段校验
需要两个字段联动判断时(如确认密码必须等于密码),用 register_validation 不方便,改用结构体验证函数:
var _ = validate.RegisterStructValidation(func(sl validator.StructLevel) {
u := sl.Current().Interface().(CreateUser)
if u.Password == "" {
return
}
// 这里可以读取另一个字段做二次确认
}, CreateUser{})
三个实践要点
- 校验器建议做成包级单例并在 init 里注册自定义规则,避免每次请求重复注册。
- 给前端返回的错误用字段名做 key,不要直接抛英文 ValidationErrors,否则排查成本高。
- 仅校验"进入系统的边界":内部函数之间传递的、已经可信的结构体不必再校验,省下性能。
T
test1
文章作者
为什么不用手写 if 判断 接收外部请求时,参数校验最容易散落成一堆 if err != nil。go-playgro...
- 分类
- 技术
- 发布时间
- 2026-10-05
- 字数
- 约 2105 字
- 阅读
- 1 次
