restful 路由必须用复数资源名(如 /users),uri 应指向资源集合,操作语义由 http 方法承载;http 状态码需精准表达语义(如 422 校验失败、429 限流);json 响应须规范 struct tag 防精度丢失与敏感字段泄露;中间件顺序应为 recovery→logging→auth→handler,确保 panic 可捕获。

RESTful 路由命名必须用复数资源名,别用 UserHandler 这种单数伪 REST
很多人一上来就写 /user/{id},看起来简洁,但违反 REST 核心原则:URI 应该指向资源集合,操作语义由 HTTP 方法承载。/user 是单个实体,/users 才是可被增删改查的资源集合。
实操建议:
-
GET /users→ 列表(支持limit/offset查询参数) -
POST /users→ 创建新用户(body 传 JSON) -
GET /users/{id}→ 获取单个({id}必须是 path 参数,不是 query) -
PATCH /users/{id}→ 局部更新(别用PUT,除非你真想强制全量替换) -
DELETE /users/{id}→ 删除,返回204 No Content
容易踩的坑:GET /user?id=123 看似能用,但无法被标准 OpenAPI 工具识别为资源操作;PUT /users/{id} 要求客户端提供完整字段,否则可能意外清空字段——Go 的 struct 默认零值会覆盖 DB 原值。
HTTP 状态码不能全用 200 OK 或 500 Internal Server Error
Go 的 http.ResponseWriter 默认状态码是 200,不显式设置就永远“成功”。但客户端靠状态码驱动行为(比如重试、跳转、提示),乱用会导致前端逻辑错乱或埋下监控盲区。
立即学习“go语言免费学习笔记(深入)”;
常见错误现象:
- 创建失败返回 200 + 错误 JSON → 前端以为成功,接着调用后续接口炸了
- 数据库连接失败返回 500 → 实际应是 503 Service Unavailable(临时不可用),500 应留给未捕获 panic
- 找不到资源返回 200 + null → 应是 404 Not Found
实操建议:
- 用
w.WriteHeader(http.StatusCreated)配合POST /users - 用
http.StatusUnprocessableEntity(422)替代 400 处理校验失败(如 email 格式不对) - 用
http.StatusTooManyRequests(429)做简单限流,别只打日志 - 所有 error handler 统一包装成
ErrorResponse{Code: "invalid_email", Message: "邮箱格式错误"},避免裸奔 error 字符串
JSON 序列化必须处理 Go struct 字段标签和零值陷阱
Go 的 json.Marshal 对 struct 字段默认导出、零值、嵌套结构非常敏感。一个没加 json: tag 的字段可能暴露敏感数据,一个没设 omitempty 的 int 字段可能把 0 当有效值传出去。
使用场景:用户注册返回 User 结构体,但 DB 主键 ID 是 int64,前端 JS 会丢失精度;密码字段绝不能出现在响应里。
实操建议:
- 所有 API 响应 struct 显式声明
jsontag:ID int64 `json:"id,string"`(转字符串防 JS 精度丢失) - 可选字段加
omitempty:Name string `json:"name,omitempty"` - 敏感字段用
-排除:Password string `json:"-"` - 别直接返回 DB model struct,定义专用的
UserResponse,字段按需裁剪
性能影响:频繁反射解析 tag 有微小开销,但远小于 JSON 解析本身;用 github.com/mailru/easyjson 可预生成序列化代码,不过多数服务没必要提前优化。
中间件顺序错了,Recovery 就拦不住 panic
Go 的 net/http 中间件是洋葱模型,外层先执行、内层后执行。如果把 logging 放最外,recovery 放最内,panic 发生时 logging 已经写了开头日志,但 recovery 永远收不到——因为 panic 把控制权直接交给了最外层的 defer。
正确顺序必须是:recovery → logging → auth → handler
实操建议:
- 用
func(next http.Handler) http.Handler模式写中间件,别用闭包链式调用(易错序) -
Recovery中间件必须用defer+recover(),且recover()后立即写 500 响应并 return,否则 panic 会继续向上冒泡 - 日志中间件里记录
r.URL.Path和r.Method即可,别试图读r.Body(只能读一次,读完 handler 就拿不到) - auth 中间件检查失败时,必须显式
w.WriteHeader(http.StatusUnauthorized),别只 return
兼容性注意:Go 1.22+ 的 http.ServeMux 支持 HandleFunc 注册中间件,但老项目用 gorilla/mux 或 chi 时,顺序逻辑完全一样——顺序错了,再好的中间件也白搭。
最常被忽略的是:panic 可能来自第三方库(比如 json.Unmarshal 遇到超长嵌套),不加 recovery 就直接 502 给网关,连日志都来不及打。别指望测试覆盖所有边界,生产环境 recovery 是底线。










