需在init()或main()开头调用validation.AddRule("Chinese", isChinese)注册,函数签名必须为func(interface{}, string) bool,tag中直接写Chinese(无括号无参数),字段须导出且为基本类型。

怎么注册自定义验证函数
Beego 的 validation 包允许你用 validation.AddRule() 注册全局验证函数,后续就能在 struct tag 的 valid 标签里直接调用。它本质是把一个函数名映射到一个校验逻辑,不支持传参,所以适合「布尔型判断」类规则(比如是否为中文、是否含敏感词)。
常见错误现象:注册后 struct tag 里写错函数名,或函数签名不符,导致静默跳过、Valid() 返回 true 却没触发校验。
- 函数必须接收两个参数:
interface{}(字段值)和string(可选参数,通常不用),返回bool - 注册需在程序启动早期完成(如
init()或main()开头),不能在 handler 里反复注册 - 函数名在 tag 中区分大小写,且不能含空格或特殊符号
示例:
func isChinese(v interface{}, param string) bool {
s, ok := v.(string)
if !ok {
return false
}
for _, r := range s {
if r < 0x4e00 || r > 0x9fff {
return false
}
}
return true
}
func init() {
validation.AddRule("Chinese", isChinese)
}
之后就能在结构体中使用:
type User struct {
Name string `valid:"Required;Chinese"`
}
struct tag 里怎么调用自定义规则
注册成功后,在字段的 valid tag 里直接写函数名即可,多个规则用分号 ; 分隔,无需括号,也不支持传参——这点和内置规则(如 Range(1,10))不同。
容易踩的坑:
- 误写成
Chinese()或Chinese("xxx")→ 解析失败,整个 tag 被忽略 - 自定义函数返回
true表示「通过」,false表示「失败」,别反了 - 如果字段类型不是函数期望的(比如
isChinese传入int),会直接返回false,但不会报 panic
对比:
// ✅ 正确:无参调用 Name string `valid:"Required;Chinese"` // ❌ 错误:加括号或参数 Name string `valid:"Chinese()"` Name string `valid:"Chinese(allowEmpty)"`
需要传参的场景怎么办
Beego 原生 valid tag 不支持向自定义函数传参。如果你需要动态阈值(比如「用户名长度至少 N 位」)、上下文依赖(比如「密码不能等于用户名」),就不能靠 tag 驱动,得退回到手动验证模式。
推荐做法是:保留结构体 tag 做基础校验(非空、格式),再在 controller 或 service 层用 validation.Validation 实例补全逻辑。
- 用
validtag 处理通用、静态规则(Required、Email、Chinese) - 用代码调用
valid.Match()、valid.Custom()或自定义函数处理带参/跨字段逻辑 - 避免在结构体里塞
map、slice等非基本类型字段,否则整个Valid()可能静默失效
例如密码一致性检查:
u := &User{}
if err := this.ParseForm(u); err != nil {
return
}
valid := validation.Validation{}
valid.Required(u.Password, "password")
valid.Required(u.CfmPwd, "cfmpwd")
if u.Password != u.CfmPwd {
valid.SetError("cfmpwd", "两次输入的密码不一致")
}
if valid.HasErrors() {
// 处理错误
}
为什么 Valid() 有时总返回 true 却没错误
这是 Beego validation 最隐蔽的问题之一:只要结构体里存在无法反射处理的字段(如未导出字段、map[string]string、func、channel),旧版(v1.12 及之前)会直接终止遍历,不报错也不继续校验,Valid() 就永远返回 true,Errors 为空 slice。
关键点:
- 字段必须是导出的(首字母大写) + 基本类型(
string、int、bool、time.Time等) - 别在待验证结构体里放
Errors map[string]string这类辅助字段,移到局部变量或方法返回值里 - 调试时可先打印
reflect.TypeOf(u)确认字段类型,再逐个注释字段排查
真正可靠的验证入口,其实是手动创建 validation.Validation{} 并显式调用各方法——它绕过了 struct tag 反射的全部陷阱。


















