自定义宏必须提前用app.Macros().Register注册,不能在路由中临时定义;未注册的宏名(如alphanumdash)会导致路由匹配失败且无报错,注册须在app.Listen()前完成且仅一次。

自定义宏必须用 app.Macros().Register 预注册,不能写在路由里临时生效
Iris 的 {id:int min(1) max(999999)} 这类带约束的宏,底层依赖 app.Macros() 注册的验证逻辑。你不能在 app.Get("/user/{id:int min(1)}", handler) 里直接“发明”新规则——它只认预注册过的宏名和参数格式。
比如想支持 {slug:alphanumdash}(只允许字母、数字、短横线),就得提前注册:
app.Macros().Register("alphanumdash", func(macro *iris.Macro) {
macro.Type = "string"
macro.Regex = `^[a-zA-Z0-9\-]+$`
})
注册后才能在路由中安全使用:app.Get("/post/{slug:alphanumdash}", postHandler)。漏掉注册,Iris 启动时不会报错,但该路由永远匹配失败。
- 宏名必须全小写,且不能含下划线或数字开头
-
macro.Regex是唯一校验入口,macro.Type只影响后续ctx.Params().GetXXX()的转换行为 - 注册必须在
app.Listen()之前,且只能执行一次;重复调用会 panic
validator.RegisterValidation 扩展 binding 标签,不是加个函数就行
结构体字段用 binding:"custom_rule" 时,Iris 会把校验委托给 go-playground/validator/v10 实例。但这个实例得是你自己初始化并赋给 app.Validator 的,否则 binding 标签完全不触发。
正确流程是:
v := validator.New()
v.RegisterValidation("phone", func(fl validator.FieldLevel) bool {
num := strings.TrimSpace(fl.Field().String())
return regexp.MustCompile(`^1[3-9]\d{9}$`).MatchString(num)
})
app.Validator = v
之后结构体才能用:
type User struct {
Phone string `json:"phone" binding:"required,phone"`
}
- 函数签名必须是
func(fl validator.FieldLevel) bool,返回true表示通过 -
fl.Field().String()是最常用取值方式,注意它对非字符串类型会 panic,建议先用fl.Field().Kind()判断类型 - 注册名
"phone"区分大小写,且不能和内置规则(如email、min)重名
路由宏和 binding 标签的错误处理机制完全不同
这是最容易混淆的点:宏匹配失败(比如 /user/abc 对应 {id:uint64})直接返回 404,handler 根本不执行;而 binding 校验失败(比如 JSON 里 "age": -5)仍会进 handler,只是 ctx.ReadJSON(&u) 返回 error。
- 宏失败是路由层拦截,不消耗 handler 资源,适合强类型/范围兜底
- binding 失败是业务层校验,能返回精细错误信息(如
"age: must be greater than or equal to 0"),但需手动处理 error 并终止后续逻辑 - 两者不互斥——推荐组合用:
/user/{id:uint64}卡住非法路径,结构体 binding 再校验请求体字段
自定义规则别碰 ctx.Params().Get 和 ctx.URLParam 的原始值
一旦用了宏或 binding,就别再手动调 ctx.Params().Get("id") 或 ctx.URLParam("page") 去做二次校验。原因很实际:
-
ctx.Params().Get("id")在{id:uint64}匹配失败时返回空字符串,你还要额外判空+转类型,反而绕过宏的保护 -
ctx.URLParam("page")拿到的是原始字符串,strconv.Atoi失败时 panic,而ctx.URLParamInt("page")才返回(int, error)可控结果 - binding 已覆盖 JSON/Form 请求体,再去读
ctx.Request().Body会导致 body 被 consume 两次,ReadJSON直接返回io.EOF
真正该盯住的,是宏注册是否遗漏、validator 实例是否赋值成功、以及 binding 错误是否被 ctx.StopExecution() 正确中断——这些地方一漏,自定义规则就形同虚设。


















