parseform与validation.struct必须分开调用:先this.parseform(&u)绑定数据,再valid.struct(&u)校验;结构体须为值类型、字段首字母大写且带form tag;复杂校验需实现valid方法;多选字段用getstrings;错误信息应映射为语义化键传至模板。

ParseForm 和 validation.Struct 必须分开调用,不能自动联动
Beego 的 ParseForm 只做数据绑定,不触发任何校验;validation.Struct 只读结构体字段的 valid tag 并执行规则,但不会从请求中取值。两者是独立流程,漏掉任一环节都会导致“看似没报错,实则没验证”。
常见错误写法:this.ParseForm(&u) 之后直接返回成功,没调 valid.Struct(&u);或者只调 valid.Struct(&u) 却没先绑定,结构体字段还是零值,验证永远通过。
- 正确顺序:先
this.ParseForm(&u)→ 再valid := validation.Validation{}; valid.Struct(&u) - 结构体变量必须是值类型(
u := User{}),不能是指针(u := &User{}),否则ParseForm会因反射失败而静默跳过字段填充 - 所有要绑定的字段必须首字母大写(导出),且带
form:"xxx"tag,例如Username string `form:"username"`
StructTag 里 valid 规则只支持内置函数,复杂逻辑得靠 ValidFormer
valid:"Required;Email;MaxSize(100)" 这类写法只认 Beego validation 包内置的函数名,比如 Required、Email、Mobile、Match(正则必须用 /.../ 包裹),但像 Min、Max、Message 这些不能直接写进 tag,否则会 panic 或被忽略。
业务级校验——比如“密码和确认密码必须一致”“用户名不能含 admin”——必须实现 Valid 方法:
- 给结构体定义
func (u *User) Valid(v *validation.Validation) - 该方法会在
valid.Struct(&u)所有 tag 规则通过后自动调用 - 内部用
v.SetError("FieldName", "错误信息")主动注入错误,valid.HasErrors()就能捕获
GetString/GetInt 是替代方案,适合简单场景或混合参数来源
当表单字段少、或需要混用路由参数(:id)和查询参数(?q=xxx)时,GetString 比 struct 绑定更直接。
但要注意:
-
GetString("name")返回空字符串不等于“参数缺失”,必须配合 err 判断:name, err := c.GetString("name"); if err != nil { /* 缺失 */ } -
GetInt("age")对非数字输入返回0, err,不是静默转 0;别用if age == 0判定无效 - 字段名严格大小写匹配,
GetString("Name")拿不到name=xxx - 多选字段(checkbox / multiple select)必须用
GetStrings("hobby"),GetString只取第一个
错误信息怎么传给前端?别硬编码 Key,用模板变量映射
valid.Errors 里的 err.Key 是类似 "Username.Required" 这种格式,直接塞进模板容易耦合、难维护。建议统一转成语义化变量名:
- 遍历
valid.Errors,按err.Key前缀提取字段名(如"Username") - 构造 map:
errs["VerifyUsername"] = err.Message,然后this.Data["VerifyUsername"] = errs["VerifyUsername"] - 模板里写
{{.VerifyUsername}},而不是{{.Username.Required}} - 这样前端不用关心后端验证规则变化,字段重命名也不影响模板
真正容易被忽略的是:struct 字段默认零值(比如 Age int 默认是 0)会让“用户传了 0”和“根本没传”无法区分。如果字段可选,要么改用指针类型(*int),要么加额外标志字段,再配合 GetString 的 err 判断来识别缺失。











