直接用 c.Params() 不算参数校验,因 Fiber 默认不拦截非法路径段、不验证范围/格式/必填性,易致 panic 或安全漏洞。

为什么直接用 c.Params() 不算参数校验
很多人以为从 c.Params("id") 或 c.Query("page") 拿到值就完事了,其实这只是“取值”,不是“校验”。Fiber 默认不拦截非法路径段(比如 /user/abc 仍能匹配 /user/:id),也不验证数字范围、长度、格式或必填性。一旦业务逻辑基于未校验的字符串做数据库查询或类型转换,轻则 500 panic,重则 SQL 注入或空指针。
用 fiber.Validate() 做结构体绑定校验
Fiber v3 内置了 fiber.Validate() 中间件,它依赖 Go 标准库 encoding/json 和 struct tag,适合 query/body/form 场景。关键点不是“有没有”,而是怎么配才不踩坑:
- 必须显式调用
c.Struct(&v)或c.BodyParser(&v)触发校验;Validate()本身只是注册校验器,不自动解析 - struct 字段必须首字母大写(导出),且 tag 要写对:
`json:"page" validate:"required,min=1,max=100"`—— 注意validatetag 不是binding - query 参数要用
c.QueryParser(&v),body 用c.BodyParser(&v),别混用;form-data 需确保 Content-Type 是application/x-www-form-urlencoded - 错误响应默认是 400 + JSON:
{"message":"Validation failed","errors":[{"field":"page","message":"must be greater than or equal to 1"}]},可自定义Config.ErrorHandler
路由参数(:id)怎么校验
Radix tree 本身不校验 :id 内容,但你可以用 Fiber v3.2+ 的 RegisterCustomConstraint 实现路径级约束:
- 先注册规则:
app.RegisterCustomConstraint("uint", func(s string) bool { _, err := strconv.ParseUint(s, 10, 64); return err == nil }) - 再在路由里用:
app.Get("/user/:id<uint>", handler)</uint>—— 这样/user/abc直接 404,根本不会进 handler - 注意:约束函数必须返回
bool,且不能 panic;ParseUint比strconv.Atoi更安全(避免负数误判) - 不要试图在 handler 里用正则重校验
:id,那已失去约束意义,且增加延迟
自定义校验逻辑放在哪最合理
复杂场景(如手机号归属地查表、邀请码实时有效性)不适合 struct tag,得手写逻辑。但位置很关键:
- 别塞进中间件全局校验 —— 大部分接口不需要,白耗资源
- 别全堆在 handler 开头 —— 可读性差,复用难,测试难
- 推荐做法:封装成独立函数,接收
fiber.Ctx并返回error,例如:func validateInviteCode(c fiber.Ctx) error { code := c.Query("code"); if !isValid(code) { return fiber.ErrBadRequest } - 在路由注册时组合:
app.Get("/join", validateInviteCode, handler)—— 顺序执行,任一失败即中断 - 这种写法让校验逻辑可单元测试、可复用、可审计,也符合 Fiber 的 handler 链式设计本意
真正容易被忽略的是约束和校验的分层:路径约束(:id<uint>)管格式,结构体校验(validate:"required")管语义,业务函数(validateInviteCode)管状态。三者缺一不可,但混在一起就谁都管不好。


















