revel 参数校验必须结合 validation 上下文或结构体绑定+validator,避免仅用 c.params.get() 手动判断;前者适合简单表单,后者用于生产级 json/嵌套校验;混合参数需统一收拢结构体校验;注意校验时机与中间件顺序。

Revel 框架的参数校验不能只靠 c.Params.Get() 后手动 if 判断,必须结合其内置 Validation 上下文机制或结构体绑定 + 外部校验器(如 go-playground/validator),否则会漏掉类型转换失败、空值边界、多字段联动等真实问题。
用 Revel.Validation 做基础字段级校验
Revel 自带的 Validation 是轻量、同步、控制器内联的校验方式,适合简单表单和快速原型。它不依赖反射,也不要求结构体导出,直接对传入的变量做断言。
-
c.Validation.Required(value)检查非空(对 string 是 len > 0,对 int 是非零值,对指针是 != nil) -
c.Validation.MinSize(value, n)和c.Validation.MaxSize(value, n)仅对 string 或 slice 有效,别误用于 int -
c.Validation.Match(value, *regexp.Regexp)要求 value 是 string 类型,传 int 会 panic - 所有校验结果默认以变量名(如
username)为 key 存入c.Validation,模板中用{{.errors.username.Message}}渲染 - 调用
c.Validation.HasErrors()后必须配c.Validation.Keep()+c.FlashParams(),否则重定向后错误丢失
用结构体绑定 + go-playground/validator/v10 做生产级校验
当接口需处理 JSON Body、复杂嵌套、或统一错误格式时,Revel.Validation 显得力不从心。此时应放弃控制器方法参数自动绑定,改用显式结构体 + 第三方 validator。
- 定义导出结构体,字段加
validatetag:type CreateUserReq struct { Username string `json:"username" validate:"required,min=4,max=15,alphanum"` } - 在控制器中手动解析并校验:
var req CreateUserReq; if err := c.Params.BindJSON(&req); err != nil || validator.New().Struct(&req) != nil { ... } -
c.Params.BindJSON()会自动调用json.Unmarshal,但不会触发 validator;必须显式调用Validate.Struct() - 注意:Revel 的
BindJSON不支持time.Time自动解析,需配合自定义UnmarshalJSON方法或预处理字符串 - 错误提示无法直接映射到模板
.errors,需手动构造 map 并塞进c.RenderArgs传递
Query / Form / Path 参数混合校验怎么写才不重复?
Revel 把所有参数都塞进 c.Params,但 Validation 只能校验你传进去的变量,不会自动扫描 c.Params.All()。混合场景必须手动提取 + 统一校验。
- 路径参数(如
/user/:id)走c.Params.Get("id")→ 转int64→ 丢给 validator 结构体字段 - 查询参数(如
?page=1&size=20)同样用c.Params.Get()提取,别用c.Params.Values["page"]—— 后者返回[]string,易引发越界 - 表单参数(
application/x-www-form-urlencoded)不能依赖c.Params.Form(它始终为 nil),必须先c.Request.ParseForm(),再用c.Request.PostFormValue("name")或c.Params.Get("name") - 推荐做法:把所有要校验的参数收拢到一个结构体里,在 Action 开头统一 Bind + Validate,而不是分散在多个
c.Params.Get()后各自 if
容易被忽略的坑:校验时机与中间件顺序
Revel 的 ValidationFilter 默认在 ParamsFilter 之后、ActionInvoker 之前执行,但它只恢复上一次请求留下的 Flash 验证错误,**不参与本次请求的实时校验**。这意味着:
- 你在控制器里写的
c.Validation.Required(x)是纯内存操作,和ValidationFilter无关 - 如果你在自定义中间件里提前设置了
c.Result(比如鉴权失败返回 401),ValidationFilter根本不会运行,但你的控制器校验代码也不会执行 —— 这个控制流断裂点常被忽略 - 对 multipart 表单(含文件上传),必须在调用任何
c.Request.FormValue前先c.Request.ParseMultipartForm(32 ,否则后续读取为空且无报错 - 校验失败时不要只返回
c.RenderError(),它不设 Content-Type,前端 fetch 可能解析失败;应显式c.Response.WriteHeader(400)+c.RenderJSON(map[string]string{"error": "xxx"})











