kratos微服务需在controller层封装统一响应格式response[t],含code、message、data三字段;提供ok/okmessage/fail函数,fail须用kratos/errors.newf;handler返回nil, nil避免重复响应;panic由recovery.recovery中间件兜底为标准500响应。

在Kratos微服务项目中实现统一响应格式,是为了让所有HTTP接口返回结构一致的code、message、data三元组,避免前端反复适配不同形态的错误体或空data字段。这一步必须在Controller层完成封装,不能依赖中间件自动注入,因为中间件无法感知业务逻辑是否成功、该返回什么code和message。
定义泛型响应信封结构
在api/v1/response.go中声明生产级响应结构:
type Response[T any] struct {
Code int `json:"code"`
Message string `json:"message"`
Data T `json:"data"`
}
这个结构强制所有响应走同构契约,前端可全局用res.data?.user安全取值,无需判断res.data是否存在。
封装OK/OKMessage/Fail三类响应函数
在internal/handler/response.go中实现:
func OK[T any](c context.Context, data T) {
kgin.Data(c, http.StatusOK, Response[T]{Code: 0, Message: "success", Data: data})
}
func OKMessage(c context.Context, msg string) {
kgin.Data(c, http.StatusOK, Response[any]{Code: 0, Message: msg, Data: nil})
}
func Fail(c context.Context, httpStatus, code int, msg string) {
kgin.Error(c, errors.Newf(code, msg))
}
【注意:Fail必须用kratos/errors.Newf构造错误,不能直接c.JSON()】 否则X-Code/X-Reason响应头不会写入,监控系统无法识别错误类型。
在Handler中调用响应函数
以用户查询接口为例:
func (h *UserHandler) GetUser(ctx context.Context, req *v1.GetUserRequest) (*v1.GetUserResponse, error) {
u, err := h.uc.GetUser(ctx, req.Id)
if err != nil {
Fail(ctx, http.StatusNotFound, 40401, "user not found")
return nil, err
}
OK(ctx, &v1.User{Id: u.Id, Name: u.Name})
return nil, nil
}
这里返回nil, nil是关键——kratos要求Handler返回error时才触发全局recover,而实际响应已由OK或Fail提前写出。若此处返回u, nil,kratos会再执行一次默认JSON序列化,造成重复响应。
统一处理未捕获panic
在cmd/server/main.go的app初始化阶段注册recover中间件:
app := kratos.New(
kratos.Name("user"),
kratos.Version("v1.0.0"),
kratos.Server(
httpSrv,
grpcSrv,
),
kratos.Middleware(
recovery.Recovery(),
logging.Logging(),
),
)
kratos的recovery.Recovery()会在panic后自动调用kgin.Error(c, errors.InternalServer("panic")),确保即使代码崩溃也返回标准500响应体,而不是空白页或HTML错误。











