Kratos业务状态码和HTTP状态码分离

起因 在我们使用 kratos 开发 web 应用时,由于 kratos 默认设计导致我们在自定义错误时,访问 api 时,相应的状态码是以自定义的 error code 作为 http status code 表现的。 如果我们想将 业务状态码和 HTTP 状态码分离,即所有的 http status code 都为 200, 实际的业务状态码以在 response 中响应的 code 为主。 我们该如何实现呢? 有2种方法,一种是自定义中间件将错误进行特别处理,另一种是自定义 Encoder。 本篇文章主要就是介绍 Encoder 方法。 自定义 Encoder 根据我们的需要,我们需要自定义 2 个 Encoder, ErrorEncoder 和 ResponseEncoder 函数。 需要的参数类型分别如下: // ResponseEncoder with response encoder. func ResponseEncoder(en EncodeResponseFunc) ServerOption { return func(o *Server) { o.enc = en } } // ErrorEncoder with error encoder. func ErrorEncoder(en EncodeErrorFunc) ServerOption { return func(o *Server) { o.ene = en } } // EncodeResponseFunc is encode response func. type EncodeResponseFunc func(http.ResponseWriter, *http.Request, interface{}) error // EncodeErrorFunc is encode error func. type EncodeErrorFunc func(http.ResponseWriter, *http.Request, error) 1 自定义 Response 结构 定义一个 Response 的结构体 ...

三月 27, 2022 · overstarry

kratos 自定义handler func 没有请求日志的问题及解决

出现的问题 最近在使用 kratos 开发 api 的时候, 由于通过 proto 生成的 server handler 不符合业务的需求, 需要通过自定义 handlerFunc 来定义接口。 在开发中为了程序的可观测性,我使用了 kratos 提供的 logging 中间件。 在使用的过程中, 我发现自定义的 HandlerFunc 的请求日志没有显示, 而 proto 生成的请求正确显示了。 问题的原因 为了找到出现这种情况的原因, 我在官方的 Github 仓库提了个 issue(#1566), 得到了维护人员的解答。 出现这种情况的原因是 自己定义的 handlerFunc 不走 middleware 中间件, 需要自定义 http filter 才能解决。 解决 经过对 http filter 例子的简单研究, 参考了官方的 logging 中间件, 我自己实现了 logging filter. 代码: package middleware import ( "fmt" "net/http" "time" "github.com/go-kratos/kratos/v2/errors" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport" ) func Server(logger log.Logger) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var ( code int32 reason string kind string operation string ) ctx := r.Context() startTime := time.Now() if info, ok := transport.FromServerContext(ctx); ok { kind = info.Kind().String() operation = info.Operation() } next.ServeHTTP(w, r) if se := errors.FromError(nil); se != nil { code = se.Code reason = se.Reason } level, stack := extractError(nil) _ = log.WithContext(ctx, logger).Log(level, "kind", "server", "component", kind, "operation", operation, "args", extractArgs(r), "code", code, "reason", reason, "stack", stack, "latency", time.Since(startTime).Seconds(), ) }) } } // extractArgs returns the string of the req func extractArgs(req interface{}) string { if stringer, ok := req.(fmt.Stringer); ok { return stringer.String() } return fmt.Sprintf("%+v", req) } // extractError returns the string of the error func extractError(err error) (log.Level, string) { if err != nil { return log.LevelError, fmt.Sprintf("%+v", err) } return log.LevelInfo, "" } 顺利的显示了请求的日志: ...

十月 22, 2021 · overstarry