Go mod 好菜系列 - 0x07 validator 别再手搓参数校验了

详细讲 validator 在 Go 项目里为什么高频、标签校验怎么写、适合放在请求层还是业务层,以及什么时候别迷信自动校验。

接口一多以后,你很快就会发现一件事:参数校验这种活,写起来没有技术含量,但量特别大,而且特别容易重复。用户名不能为空、邮箱格式要对、年龄不能小于 0、分页参数得有上限……这些东西如果每个接口都手写 if,很快就会让人产生一种“我今天是不是在做表单保安”的感觉。

validator 是来解决什么的

它最核心做的事,就是把一部分通用的参数合法性检查,从一堆手写判断里抽出来,让你能用结构体 tag 的方式声明校验规则。

这类规则特别适合它:

  • 必填
  • 长度限制
  • 邮箱格式
  • 数值区间
  • 枚举值约束

也就是说,它不是替你做所有业务校验,而是先帮你把“参数长得像不像话”这层事接过去。

为什么它在 Go 项目里这么高频

因为很多 Go Web 项目都会和 gin、echo、fiber 之类框架一起用,而这些框架在绑定请求结构体以后,天然就会碰到“下一步我要不要校验”的问题。validator 正好卡在这里。

最常见的写法

type CreateUserReq struct {
    Name  string `json:"name" validate:"required,min=2,max=32"`
    Email string `json:"email" validate:"required,email"`
    Age   int    `json:"age" validate:"gte=0,lte=150"`
}
validate := validator.New()

req := CreateUserReq{
    Name:  "Raymond",
    Email: "ray@example.com",
    Age:   18,
}

if err := validate.Struct(req); err != nil {
    return err
}

这套写法的好处特别直观:字段定义和字段约束放在一起,结构是收拢的,不用跳来跳去找。

常见 tag 很值得记住

  • required
  • min / max
  • gte / lte
  • oneof
  • email
  • omitempty

比如状态字段:

Status string `json:"status" validate:"oneof=draft published archived"`

这类声明式约束很适合把一些常规输入边界固定下来。

和 gin 搭在一起为什么很常见

因为 gin 的请求绑定本来就是按结构体走的,所以很多项目会这样用:

func createUser(c *gin.Context) {
    var req CreateUserReq
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    if err := validate.Struct(req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    c.JSON(200, gin.H{"ok": true})
}

这一层做的其实是“输入校验”,不是完整业务规则判断。

别把所有校验都往 validator 里塞

这是很重要的一点。validator 特别擅长的是字段级、结构级的格式和约束检查,但它并不适合承接复杂业务逻辑。

比如这些就不太适合全塞进去:

  • 用户名是否已存在
  • 当前用户有没有权限这么做
  • 文章发布时间是否满足业务流程

这些本质上不是“参数格式问题”,而是业务规则问题,应该更多待在 service 层。

自定义校验也能做

validate.RegisterValidation("slug", func(fl validator.FieldLevel) bool {
    value := fl.Field().String()
    return regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(value)
})

这样你就能补自己的规则。但还是那句话,别因为它支持扩展,就把整套业务判断都写成 tag 驱动迷宫。

错误信息别直接裸奔给用户

validator 返回的错误对开发者还行,对用户就未必友好了。真实项目里更常见的做法,是把这些错误整理成稳定、清楚的字段提示,而不是直接把原始报错抛前端。

小结

validator 这道菜特别像一个“省重复劳动”的工具:

  • 它适合做输入参数的通用合法性校验
  • 和 gin 这类框架结合很顺
  • 字段格式约束适合它,复杂业务判断不适合它
  • 能省很多重复 if,但别把它当业务引擎

下一篇我们讲 jwt。虽然前面在另一套系列里聊过鉴权思路,但这次会更聚焦到模块层面:项目里到底怎么把 JWT 用得像样。

Read more

Harness Engineering:从驾驭模型到构建 AI 工厂

Harness Engineering:从驾驭模型到构建 AI 工厂

2026 年,AI 编程工具的竞争焦点发生了根本性转变:决定 AI 助手好不好用的,不再是模型本身,而是包裹在模型外面的那层"Harness"。同一个模型,在不同的 Harness 下,性能差距可以达到 78% vs 42%。 这篇文章将带你深入了解 Harness Engineering——这个正在重新定义 AI 工程实践的新兴领域。 一、什么是 Harness Engineering? Harness 的字面意思是"马具/缰绳"——用来驾驭一匹强壮但不受控的马。在 AI 语境下,Harness 就是 LLM 之外的一切:工具定义、记忆系统、权限模型、反馈循环、文档规范、多

By Fuyu Jia

Claude Code CLI + Ralph:让 AI 自动完成大型编程任务的终极方案

TL;DR 当你的编程任务大到一个 AI 对话窗口装不下时,Ralph 会帮你把任务拆成小块,让 Claude Code CLI 一个接一个地自动完成——每轮都用全新的上下文窗口,不会越写越糊涂。 一、什么是 Ralph? Ralph 是一个开源项目(GitHub 16k+ Stars),基于 Geoffrey Huntley 提出的 "Ralph Pattern" 构建。它的核心理念很简单: 不要让 AI 在一个漫长的会话里做完所有事情,而是把大任务拆成小故事,每个故事用一个全新的 AI 实例来完成。 这解决了 AI 编程中最常见的痛点——上下文窗口耗尽。当对话越来越长,AI 的输出质量会明显下降。Ralph 通过「每轮一个新实例」的方式,

By Fuyu Jia