Kratos 默认HTTP响应强制套壳为{"code":0,"message":"ok","data":{...}},由transport层response middleware处理;需自定义http.Codec绕过套壳,或替换ErrorHandler调整错误格式。

返回结构被强制套壳?看 kratos/api 包的默认封装逻辑
Kratos 默认所有 HTTP 响应都会走 api/v1.Response(或类似命名的统一包装结构),比如:{"code":0,"message":"ok","data":{...}}。这不是中间件加的,而是由 transport/http/http.go 中注册的 http.Server 自动调用 encoding/json 序列化前,经由 transport/http/middleware/response.go 的默认包装器处理的。
关键点在于:这个包装行为发生在 transport 层,与 service 层返回值无关。你写 return &User{...},框架仍会把它塞进 data 字段里。
- 不希望套壳?必须显式绕过默认响应编码器
- 想保留套壳但改字段名(如把
code改成status)?需替换默认的http.Codec - 全局禁用?不推荐——会丢失错误码透传、trace ID 注入等能力
自定义 HTTP 响应格式:重写 http.Codec 实现
真正可控的入口是 http.NewServer 的 http.WithCodec 选项。Kratos 的 http.Codec 是一个接口,负责请求反序列化 + 响应序列化。默认实现是 http.JSONCodec,它内部硬编码了包装逻辑。
要改格式,得自己实现一个 Codec:
type CustomCodec struct{}
func (c *CustomCodec) Marshal(v interface{}) ([]byte, error) {
// 不再套壳,直接序列化业务数据
return json.Marshal(v)
}
func (c *CustomCodec) Unmarshal(data []byte, v interface{}) error {
return json.Unmarshal(data, v)
}
func (c *CustomCodec) Name() string {
return "json"
}
然后在构建 HTTP Server 时注入:
hs := http.NewServer(
http.Address(":8000"),
http.Middleware(...),
http.WithCodec(&CustomCodec{}), // ← 关键替换
)
- 注意:此时所有 handler 返回值都必须是最终要输出的结构体,不能再依赖
data字段自动包裹 - 如果部分接口要套壳、部分不要,建议拆成两个
http.Server实例,或在 middleware 中做类型判断(不推荐,易混乱) - 别忘了同步修改错误响应逻辑——
http.ErrorHandler默认也依赖原 codec,需一并重写
只改状态码映射?调整 http.ErrorHandler 更安全
很多场景其实只是想让 400 错误返回 {"status":400,"msg":"invalid param"},而非默认的 {"code":40000,"message":"..."}。这种需求不该动 codec,而该换 ErrorHandler。
Kratos 允许你传入自定义错误处理器:
http.NewServer(
http.ErrorHandler(func(ctx context.Context, w http.ResponseWriter, err error) {
code := http.StatusInternalServerError
msg := "internal error"
if e, ok := err.(errors.CodeError); ok {
switch e.Code() {
case errors.BadRequest:
code = http.StatusBadRequest
case errors.NotFound:
code = http.StatusNotFound
}
msg = e.Message()
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(code)
json.NewEncoder(w).Encode(map[string]interface{}{
"status": code,
"msg": msg,
})
}),
)
- 这个方式不影响正常成功响应的结构,只接管错误路径
- 务必检查
err是否实现了errors.CodeError接口,否则无法提取业务定义的错误码 - 别漏掉
w.Header().Set和w.WriteHeader,否则浏览器可能解析失败或状态码不对
Proto 定义影响 gRPC 响应,但对 HTTP gateway 无效
有人误以为改 .proto 里的 message 就能控制 HTTP 返回结构——这是错的。gRPC 原生响应结构由 proto 编译后生成的 Go 结构体决定;而 HTTP gateway(通过 google.api.http 注解暴露的接口)的 JSON 输出,是由 codec + response wrapper 共同决定的,跟 proto 里字段名是否叫 data 没有直接关系。
- proto 中定义
message UserResponse { string user_name = 1; }→ gRPC 返回就是该结构;HTTP gateway 默认仍会把它包进{"code":0,"message":"ok","data":{...}} - 想让 HTTP 直接输出
{"user_name":"xxx"}?必须配合上面的自定义Codec - proto 里加
option (google.api.http) = { post: "/user" };只影响路由和方法,不影响 JSON 字段层级
最常被忽略的一点:自定义 codec 后,http.Middleware 中的 ctx.Value 注入(比如 trace ID、用户身份)不会自动出现在响应头里,需要手动 w.Header().Set 补上。否则监控链路就断了。


















