Gin中间件中禁用BindJSON,应手动json.Unmarshal预检并重置body;binding标签在中间件无效;校验须强制执行,不可依赖Header跳过;注意并发下bytes.Buffer复用安全。

用 gin.BindJSON 会直接 panic,别在中间件里硬解码
很多刚写 Gin 的人想在中间件里调 c.ShouldBindJSON() 或 c.BindJSON() 提前校验,结果一遇到非法 JSON 就 500 —— 因为 BindJSON 内部会直接调 c.AbortWithError(400, err),而中间件里没注册错误处理逻辑时,panic 就来了。
正确做法是只做「预检」:读取原始 body、解析、出错就 Abort,不走 Bind 流程。
- 用
c.Request.Body读一次,记得用io.ReadAll后重置c.Request.Body = io.NopCloser(bytes.NewReader(data)),否则 Handler 里再读就是空的 - 别用
c.GetRawData(),它会消耗 body 且不可重放,Gin v1.9+ 已标记为 deprecated - 如果用了
gin.Recovery(),它能兜住 panic,但掩盖了真实问题,不该依赖它来“修复”校验逻辑
结构体字段加 binding 标签不如先过 json.Unmarshal
有人以为给 struct 加 json:"name" binding:"required" 就能在中间件生效,其实不行 —— binding 是 ShouldBind 系列函数才用的规则,中间件里没触发 Binding 阶段,标签压根不生效。
真正可控的预校验,是手动 json.Unmarshal 到一个空 map[string]interface{} 或基础结构体:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": "invalid JSON"})
return
}
- 这样能捕获
invalid character、unexpected end of JSON input等底层错误 - 如果后续还要进 Handler 做字段级校验(比如邮箱格式),保留原始
data字节切片,传给 Handler 复用,避免二次解析 - 注意
json.Unmarshal对超大 payload 没限制,得自己加长度检查,比如if len(data) > 1(1MB)就拒绝
Gin 中间件里不能改 c.Request.URL 或 c.Request.Header 来绕过校验
有同学试过在中间件里把 c.Request.Header.Set("X-Skip-Validate", "1"),然后在 Handler 里判断跳过校验 —— 这看似灵活,实则埋雷。
问题在于:这不是校验“是否该执行”,而是校验“能不能执行”。一旦业务逻辑依赖某个字段(比如 user_id),跳过校验等于把脏数据直接喂给业务层,后面查 DB 报错、空指针、SQL 注入风险全来了。
- 校验必须是强制的,不是可选开关;真有例外场景(如内部调试接口),应该用独立路由分组,而不是靠 header 控制流程
- 如果必须动态控制,用路径前缀区分更安全,比如
/api/v1/internal/xxx走免校验链,/api/v1/public/xxx走完整校验链 - Header 可被客户端伪造,
X-Skip-Validate这种字段在生产环境等同于后门
并发下复用 bytes.Buffer 或全局 sync.Pool 要小心
为了省内存,有人在中间件里用 sync.Pool 缓存 bytes.Buffer,但容易踩到两个坑:一是没重置 buffer 内容,上一次请求的 body 残留进下一次;二是 pool.Get() 返回的 buffer 容量可能远大于当前请求 body,导致内存浪费甚至 OOM。
- 每次从 pool 拿到 buffer 后,必须调
b.Reset(),不能只靠b.Truncate(0) - 更稳妥的做法是直接用
make([]byte, 0, 4096)预分配小切片,大部分请求 body 在几 KB 内,避免 pool 管理开销 - 如果真要用 pool,建议封装成
func() []byte工厂函数,确保返回前已清空且容量合理

















