c.Param("id")必须先判空再转换,未匹配时返回空字符串而非panic;直接strconv.Atoi会因空字符串报错,须校验非空且转换失败时返回400错误。

c.Param("id") 必须校验非空再转换
路径参数 c.Param("id") 不是查询参数,不能用 c.Query("id") 读;它在 URL 缺失时返回空字符串,直接 strconv.Atoi(c.Param("id")) 会 panic。
- 先判断
idStr := c.Param("id")是否为空,空则返回http.StatusBadRequest - 再用
strconv.Atoi转换,失败时也返回http.StatusBadRequest,不暴露原始错误 - 别依赖
user.ID != 0判断插入成功——GORM 约束冲突时user.ID仍可能是 0,必须检查err
db.Create() 必须显式判断 err 并脱敏
db.Create(&user).Error 是唯一可靠的成功判断依据。常见错误是只看 user.ID 或忽略 err,导致接口返回 200 但数据没入库,且无日志线索。
- 永远写成
err := db.Create(&user).Error,然后if err != nil分支处理 - 生产环境禁止返回
err.Error(),尤其含 MySQL 错误码、表名、字段名等敏感信息 - 若用了 struct tag 如
binding:"required",确保字段名与 JSON key 一致,否则校验不触发,直接进 DB 层失败
db.First() 查询单条前必须确认存在性
db.First(&user, id) 在查不到时不会报错,而是把 user 置为零值 struct。后续访问 user.Name 看似安全,但若字段是 *time.Time 或嵌套指针,可能 panic 或静默返回空数据。
- 正确做法是用
err := db.First(&user, id).Error,再通过errors.Is(err, gorm.ErrRecordNotFound)判断不存在 - 不要用
if user.ID == 0代替 err 判断——GORM 可能因主键类型或默认值设置让user.ID非零但记录实际未查到 - 更新前必须先查一次确认资源存在,否则
db.Save()可能插入新记录而非更新,尤其当主键未设为primary_key时
GORM 更新需控制字段范围避免意外覆盖
直接 db.Save(&user) 会更新所有非零字段,包括前端未提交的字段(如密码、状态),有安全隐患。
- 用
db.Select("Name", "Age").Updates(&user)显式指定要更新的字段 - 或用 map:
db.Where("id = ?", id).Updates(map[string]interface{}{"name": name, "age": age}) - 如果模型定义了软删除字段(如
DeletedAt),db.Delete()默认是软删,要用Unscoped().Delete()才能物理删除


















