Buffalo表单验证依赖服务端validate包,需显式调用Validate方法并传入结构体至模板;字段须导出、tag规则拼写正确(如email非email_addr);present判非零值,required判字段存在;自定义验证须实现Validate接口而非struct tag。

Buffalo 表单验证靠的是 validate 包,不是前端 JS 或中间件拦截
Buffalo 的表单验证默认走服务端、结构体绑定 + validate 标签校验,不依赖前端提交前拦截(哪怕你写了 required 属性,后端仍需独立验证)。它和 Go 的 encoding/json 绑定逻辑类似,但多了字段级规则声明和错误聚合能力。
常见错误是直接在 HTML 模板里写 if .Errors.Has "Email" 却没在 handler 里调用 Validate 方法,结果 .Errors 始终为空;或者把验证逻辑写在 Bind 后但忘了检查 err != nil 就继续执行业务逻辑。
- 必须用
buffalo.Validate实例(通常来自app.Validate)对结构体调用Validate方法,不能只靠 struct tag - 结构体字段需导出(首字母大写),且 tag 中的
validate规则要拼写正确,比如email不是email_addr - 验证失败时,
buffalo.Render自动把errors注入模板上下文,但前提是 handler 返回了err或显式调用了c.Set("errors", validate.Errors)
validate tag 支持哪些规则?常用组合怎么写
Buffalo 默认用 gobuffalo/validate,规则名和行为接近 Rails,但不支持正则自定义或条件验证(如“仅当 status=active 时校验 phone”需手写逻辑)。
最常踩的坑是混淆 present 和 required:present 表示字段非零值(字符串非空、数字非 0、bool 为 true),而 required 是字段必须存在于请求中(哪怕值为空字符串)——多数表单场景该用 present。
-
valid_email:仅检查格式,不发验证邮件;依赖net/mail.ParseAddress,对user@domain这类无点域名会失败 -
min:6/max:20:只对字符串、切片、map 生效;对 int 类型要用min:1这种数值比较 -
in:admin,user,guest:逗号分隔,注意前后不能有空格,否则guest会被当成独立值 - 多个规则用分号连接:
json:"email" validate:"required;email;max:255"
验证失败后如何保留用户已填数据并返回错误
Buffalo 的 Render 默认不会自动回填表单值,必须手动把原始数据传进模板。典型错误是验证失败后直接 c.Render(422, r.HTML(...)),但模板里 <input value="<%= user.Email %>"> 取不到值,因为 user 是空结构体。
Buffalo框架 1.0.1 版本源码包下载,适合需要错误处理改进、依赖更新、render.Download 注释和 request logger 调整的 v1 项目。
正确做法是在 handler 里先 Bind 到结构体,验证失败时原样传给模板,而不是新建一个空实例。
- 不要:
u := &User{}; c.Bind(u); if err := app.Validate.Validate(u); err != nil { c.Render(422, r.HTML("new.html")) }—— 此时模板收不到u - 要:
u := &User{}; c.Bind(u); if err := app.Validate.Validate(u); err != nil { c.Render(422, r.HTML("new.html", u)) }—— 第二个参数会作为data注入模板 - HTML 模板中用
<input name="Email" value="<%= data.Email %>">回显,data是Render传入的值 - 错误提示统一用
,不需要判断是否存在
自定义验证规则只能靠 Validate 接口,别想加 tag
Buffalo 的 validate 包不支持通过 struct tag 注册自定义函数(比如 “密码和确认密码一致”),必须实现 validator.Validator 接口,然后在验证前手动调用。这也是最容易被忽略的复杂点。
例如校验两次输入密码是否一致,不能写 validate:"eqfield=PasswordConfirmation"(那是 go-playground/validator 的写法,Buffalo 不认)。
- 新建一个方法:
func (u *User) Validate(tx *pop.Connection) (*validate.Errors, error) { ... } - 在方法里手动比对
u.Password和u.PasswordConfirmation,不相等就调用errors.Add("PasswordConfirmation", "must match Password") - 确保这个方法签名和
buffalo.Validate调用约定一致(接收*pop.Connection,返回*validate.Errors) - 调用时仍用
app.Validate.Validate(u),它会自动发现并执行该方法
这种设计让验证逻辑集中可控,但要求你清楚 Validate 方法的执行时机 —— 它在 Bind 之后、业务处理之前,且只执行一次。

















