Gin POST接口收不到JSON数据,主因是未显式调用c.ShouldBindJSON()或未启用JSON解析中间件;必须确保Content-Type为application/json、结构体字段导出且含json tag,且不可混用PostForm与ShouldBindJSON。

为什么 Gin 的 POST 接口收不到 JSON 数据?
默认情况下,Gin 不会自动解析请求体里的 JSON,必须显式调用 c.ShouldBindJSON() 或提前注册中间件启用自动绑定。常见现象是 c.PostForm("xxx") 返回空、c.GetRawData() 能读到原始字节但结构体字段全零值。
- 确保请求头包含
Content-Type: application/json,否则ShouldBindJSON会直接返回400 Bad Request - 结构体字段必须导出(首字母大写),且加
jsontag,例如:Username string `json:"username"` - 不要混用
c.PostForm()和ShouldBindJSON()—— 前者只读application/x-www-form-urlencoded,后者才处理 JSON - 调试时可用
c.Request.Body手动读取一次原始数据,但注意 Body 只能读一次,后续再调ShouldBindJSON会失败
如何安全地校验账号密码并避免明文存储?
账户管理后台最基础也最容易出错的环节:密码不能存明文,也不能用弱哈希(如 MD5)。Gin 本身不提供加密逻辑,需搭配 golang.org/x/crypto/bcrypt。
- 注册时用
bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost)生成哈希,存入数据库字段(建议长度 ≥60 字符) - 登录时用
bcrypt.CompareHashAndPassword(storedHash, inputPwd)校验,而非自己比对字符串 - 避免在日志或错误响应中暴露密码字段,
c.BindJSON(&req)后立即清空req.Password内存值 - 若需支持找回密码,只能重置(发 token 链接),绝不可“显示原密码”或“解密”
Gin 路由怎么分组并统一鉴权?
账户后台必然区分公开接口(如登录)和受保护接口(如修改邮箱、查用户列表),Gin 的 RouterGroup + 中间件是最自然的组织方式。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用
router := gin.Default()创建根路由,再通过auth := router.Group("/api/v1")划分版本前缀 - 定义鉴权中间件,检查
c.GetHeader("Authorization")是否为有效 JWT,验证失败时调c.AbortWithStatusJSON(401, ...) - 把需要登录的路由挂到
auth.Use(authMiddleware)下,比如auth.GET("/users", listUsersHandler) - 注意中间件顺序:JWT 解析中间件必须在绑定 JSON 前执行,否则
c.MustGet("user_id")取不到上下文值
数据库操作该用 GORM 还是原生 sqlx?
账户系统 CRUD 简单但要求高一致性(比如唯一用户名、事务性注册),GORM 提供便利,但容易因懒加载或隐式事务踩坑;sqlx 更轻量可控,但需手动写 SQL。
立即学习“go语言免费学习笔记(深入)”;
- 如果团队熟悉 ORM 且业务不涉及复杂关联查询,用 GORM v2 是可行的,但务必关闭全局
PrepareStmt(避免连接泄漏),并显式开启事务:db.Transaction(func(tx *gorm.DB) error { ... }) - 若只需增删改查用户表、索引明确、无嵌套结构,sqlx +
database/sql更透明,例如用tx.Exec("INSERT INTO users (...) VALUES ($1, $2)", ...)控制每一步 - 无论哪种方案,用户名/邮箱等关键字段必须建唯一索引,且在插入前做
SELECT COUNT(*)预检(GORM 的FirstOrCreate在并发下可能重复插入) - 别忘了用
context.WithTimeout包裹 DB 调用,防止慢查询拖垮整个 HTTP 请求


















