路由参数校验必须用ctx.params()而非ctx.urlparam(),因后者仅读query string;类型后缀如:uint64仅用于匹配阶段,handler中仍需非空及范围检查;query和请求体校验需手动或借助validator库,数据库唯一性应依赖unique索引而非先查后插。

路由参数校验必须用 ctx.Params(),不是 ctx.URLParam()
很多人在写 /user/{id:uint64} 这类带类型的路径时,误用 ctx.URLParam("id") 去取值,结果始终是空字符串。这是因为 ctx.URLParam() 只读 query string(比如 /user?id=123),而 RESTful 路径里的 {id:uint64} 是路径参数,必须走 ctx.Params().Get("id") 或带类型的方法。
常见错误现象:路由定义了 {uid:string},handler 里却写 ctx.Params().GetInt("uid"),直接 panic;或者拼错键名(如路由用 {user_id} 却调 Get("uid")),返回空但不报错,后续逻辑可能因空值崩掉。
-
ctx.Params().GetString("name"):安全取 string,空时返回空字符串 -
ctx.Params().GetInt64("id"):强制转 int64,失败时返回 error,适合需要强校验的场景 - 类型后缀如
:int、:bool、:uuid仅用于路由匹配阶段——不匹配就 404,但不保证 handler 内部值一定合法,仍需做非空或范围检查
Query 参数校验靠 ctx.URLParam() + 手动解析
Iris 没有内置 query 参数的结构化校验(比如像 Gin 的 binding),得自己 parse。比如 ?page=1&size=20,要确保 page 是正整数、size 在 1–100 之间,就得手动处理:
常见错误现象:直接用 strconv.Atoi(ctx.URLParam("page")),没判空或异常,遇到 ?page= 或 ?page=abc 就 panic。
- 先用
ctx.URLParam("page")取原始字符串,再判断是否为空 - 用
strconv.ParseInt(..., 10, 64)解析,并检查 error - 额外加业务约束,比如
if page ,避免负页码 - 别依赖路由参数类型(如
:int)来校验 query,它对 query 完全无效
表单/JSON 请求体校验得自己写或引入第三方
Iris 不提供开箱即用的结构体绑定与验证(比如 Go 的 validator tag 自动校验)。如果你接收 JSON,典型流程是:ctx.ReadJSON(&req) → 手动检查字段值 → 错误则 ctx.StatusCode(400) 并返回提示。
容易踩的坑:只做 ReadJSON 成功判断,忽略字段语义校验。比如用户注册传 {"username": "", "password": "123"},解包成功但业务上用户名不能为空、密码长度不够。
- 推荐搭配
go-playground/validator/v10:给 struct 加validate:"required,min=3"tag,然后调validate.Struct(req) - 别在中间件里统一做所有请求体校验——不同接口字段差异大,耦合高且难调试
- 校验失败时,用
ctx.StopWithJSON(400, map[string]string{"error": "username is required"})立即终止链,否则后续 handler 还会执行
数据库唯一性校验不能只靠代码查,要用 unique 索引
比如用户注册时检查用户名是否已存在,很多人习惯先 SELECT COUNT(*) WHERE username = ?,再决定是否 INSERT。这有竞态风险:两次并发请求可能都查到“不存在”,然后都插入成功。
真正可靠的做法是在 GORM model 上加 gorm:"unique",让数据库兜底:
type User struct {
gorm.Model
Username string `gorm:"unique;not null"`
Password string
}
然后执行 db.Create(&user),如果冲突,GORM 会返回 gorm.ErrDuplicatedKey 类型的 error,你捕获它并转成 409 即可。
注意:别忘了迁移时重建表,旧表上的索引不会自动生效;也别在事务外做“查+插”双操作,这是典型的防御性编程陷阱。











