Gin POST 路由收不到 JSON 数据主因是未显式绑定且请求不满足三条件:方法为 POST/PUT、Content-Type 为 application/json、body 为合法 UTF-8 JSON;需用 ShouldBindJSON 并确保结构体字段导出且带 json tag。

为什么 Gin 的 POST 路由收不到 JSON 数据?
默认情况下,Gin 不会自动解析请求体里的 JSON,必须显式调用 BindJSON 或启用中间件。常见现象是 c.ShouldBindJSON(&data) 返回 EOF 或空结构体,实际请求明明带了 {"sku":"A001","qty":5}。
- 确保前端发送时设置了
Content-Type: application/json - 在 handler 中不要直接读
c.Request.Body—— Gin 的绑定会消耗 body 流,重复读会失败 - 用
c.ShouldBindJSON()而非c.BindJSON(),前者不中断执行,便于自定义错误处理 - 结构体字段必须是导出的(首字母大写),且建议加
jsontag,例如:type StockMove struct { SKU string `json:"sku"` Qty int `json:"qty"` }
库存扣减逻辑怎么避免超卖?
并发请求下,单纯查库存 → 判断 → 扣减三步操作必然导致超卖。Gin 本身不提供事务能力,得靠底层数据库或原子操作兜底。
- 推荐用 SQL 的
UPDATE ... WHERE stock >= ?返回影响行数,为 0 表示库存不足 - 若用 Redis,用
DECRBY+GET组合,但注意DECRBY可能返回负值,需额外判断 - 避免在 Go 层做“先查后改”,尤其别用 map 或全局变量模拟库存 —— 没锁也没一致性保障
- 如果业务允许,可预占库存(如创建预占记录),再异步落库,但复杂度上升
gin.Default() 和 gin.New() 选哪个?
开发阶段用 gin.Default() 看似省事,但它默认加载了 Logger() 和 Recovery() 中间件 —— 日志格式固定、panic 捕获后只返回 500,不利于调试库存类业务的精确错误反馈。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 生产环境建议用
gin.New(),手动注册需要的中间件,比如只加gin.LoggerWithConfig()控制日志字段 - 库存 API 需要明确返回错误码(如 400 缺参数、404 SKU 不存在、409 库存不足),
Recovery()会吞掉你主动写的c.JSON(409, ...) - 若坚持用
Default(),至少禁用 Recovery:在它之后调用r.Use()无法覆盖已注册的 Recovery,只能换New()
如何让单个 API 同时支持入库和出库?
不必为 /in 和 /out 写两个路由,用一个 endpoint + 字段区分更易维护。关键在于动作语义清晰、校验前置。
立即学习“go语言免费学习笔记(深入)”;
- 请求体统一用
{ "sku": "...", "qty": 10, "action": "in" },action只接受"in"或"out" - 在绑定后立刻校验:
if data.Qty <= 0直接返回 400;if data.Action != "in" && data.Action != "out"也 400 - 出库时,库存检查必须在 DB 层完成(见上一节),不能仅靠
if qty > currentStock—— 并发下不可靠 - 动作字段别用布尔值(
is_in),字符串枚举更直观、扩展性更好(比如后续加adjust)

















