应定义标准响应结构体Response统一格式,Code用业务码(非HTTP状态码),Data传具体struct,Timestamp用int64,封装WriteJSON函数固定返回200并写入结构化JSON,错误也走该结构并按域划分错误码。

Go 服务返回的响应格式不统一,前端要写一堆 if type === 'xxx' 做判断,后端加个新接口又得复制粘贴重复逻辑——这不是设计,是硬编码惯性。解决办法不是堆中间件,而是从 http.HandlerFunc 的源头就约束结构。
定义标准响应结构体,别用 map[string]interface{}
用 map[string]interface{} 看似灵活,实际让 JSON 序列化失去字段约束、IDE 失去提示、单元测试难覆盖。必须定义结构体,并导出所有字段(首字母大写)。
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
Timestamp int64 `json:"timestamp"`
}
关键点:
-
Code统一用业务码(如200成功,4001参数错误),**不用 HTTP 状态码**——HTTP 状态码留给网络层,业务逻辑不该混入 transport 层语义 -
Data类型为interface{}是为了兼容任意结构,但实际使用时应传具体 struct 指针或值,避免运行时 panic -
Timestamp用int64存 Unix 时间戳,比字符串时间更省带宽、无时区歧义 - 不要加
Success bool字段——Code == 200就是成功,冗余字段只会让前端多一次判断
封装统一的 WriteJSON 辅助函数,绕过原生 json.Encoder
直接调用 json.NewEncoder(w).Encode() 容易忽略 HTTP 状态码、Content-Type、错误处理。应该封装一层,把常见路径收口。
立即学习“go语言免费学习笔记(深入)”;
func WriteJSON(w http.ResponseWriter, code int, data interface{}, message string) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK) // 注意:这里固定 200,业务码走 body.code
resp := Response{
Code: code,
Message: message,
Data: data,
Timestamp: time.Now().Unix(),
}
json.NewEncoder(w).Encode(resp)
}
使用示例:
http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
user := User{Name: "Alice", ID: 123}
WriteJSON(w, 200, user, "success")
})
注意:
- 不要在
WriteJSON里根据code设置w.WriteHeader()为 4xx/5xx——HTTP 状态码应反映请求是否被服务器接收并处理,而非业务失败。比如参数校验失败仍是 200,靠code: 4001表达 - 如果真需要返回 404 或 500(如路由不存在、DB 连接失败),应由上层 handler 显式调用
w.WriteHeader(http.StatusNotFound),再调用WriteJSON(404, nil, "not found") - 避免在
WriteJSON里做日志或监控埋点——那是 middleware 的事,函数职责要单一
错误响应也走同一结构,禁止裸 throw error
很多团队用 log.Fatal 或直接 panic 处理错误,导致无法控制返回格式。所有错误路径必须构造 Response 并写出。
func handleOrder(w http.ResponseWriter, r *http.Request) {
order, err := parseOrder(r.Body)
if err != nil {
WriteJSON(w, 4001, nil, "invalid order format") // 业务错误码
return
}
id, err := saveOrder(order)
if err != nil {
WriteJSON(w, 5001, nil, "order save failed") // 数据库错误码
return
}
WriteJSON(w, 200, map[string]int{"id": id}, "created")
}
要点:
- 错误码按域划分:
4001–4999是客户端问题(参数、权限、状态冲突),5001–5999是服务端问题(DB、RPC、缓存异常) - 不要用
http.Error()——它写的是纯文本,破坏统一 JSON 格式 - 不要在错误响应里暴露敏感信息(如 DB 错误详情),
message只给前端可展示文案,详细日志记到 server 端
最常被忽略的一点:结构体里的 Data 字段类型是 interface{},但如果你传了未导出字段的 struct(比如 type User struct { name string }),JSON 序列化会输出空对象。务必检查 struct 字段是否首字母大写、是否有 json: tag 控制导出行为。










