背景
在使用 Gin 开发 RESTful API 时,我们经常需要从 URL 的查询字符串(Query String)中获取参数,比如 GET /api/search?keyword=gin&page=1。Gin 提供了便捷的绑定方法,但如果你和我一样,习惯性地给结构体字段打上 json 标签,然后调用 c.ShouldBindQuery(),那么等待你的很可能是——
所有字段都是零值,没有任何错误提示。
错误场景再现
假设你要写一个搜索接口,接收 keyword、page、limit 三个查询参数。你写了如下代码:
typeSearchRequeststruct{Keywordstring`json:"keyword" binding:"required"`Pageint`json:"page"`Limitint`json:"limit"`}funcSearchHandler(c*gin.Context){varreq SearchRequestiferr:=c.ShouldBindQuery(&req);err!=nil{c.JSON(400,gin.H{"error":err.Error()})return}// 业务逻辑...c.JSON(200,req)}请求 URL:GET /search?keyword=golang&page=2&limit=10
服务端实际解析结果是:
{"keyword":"","page":0,"limit":0}原因分析
Gin 的绑定引擎依赖结构体字段上的 form 标签来匹配查询参数名,而不是 json 标签。
ShouldBindQuery 内部使用的是 form 标签进行映射。
只有像 ShouldBindJSON 这样的方法才会使用 json 标签。
当你只写了 json 标签时,Gin 找不到对应的字段名,于是忽略了这些字段,导致零值。而且因为绑定过程没有失败(只是没匹配到),ShouldBindQuery 返回的 err 为 nil,所以你不会收到任何错误提示——这才是最坑的地方。
解决方案
typeSearchRequeststruct{Keywordstring`form:"keyword" binding:"required"`Pageint`form:"page,default=1"`Limitint`form:"limit,default=20"`}这样 ShouldBindQuery 就能正确解析了。同时你可以保留 json 标签(如果需要返回 JSON 时使用),两者可以共存:
typeSearchRequeststruct{Keywordstring`form:"keyword" json:"keyword" binding:"required"`Pageint`form:"page" json:"page"`Limitint`form:"limit" json:"limit"`}