天津做网站那家好,定制号码需要多少钱,域名解析服务器,精准营销通俗来说是什么通过go语言原生http中响应错误的实现方法#xff0c;逐步了解和使用微服务框架 kratos 的错误处理方式#xff0c;以及探究其实现原理。 一、go原生http响应错误信息的处理方法
处理方法#xff1a;
①定义返回错误信息的结构体 ErrorResponse
// 定义http返回错误信息的… 通过go语言原生http中响应错误的实现方法逐步了解和使用微服务框架 kratos 的错误处理方式以及探究其实现原理。 一、go原生http响应错误信息的处理方法
处理方法
①定义返回错误信息的结构体 ErrorResponse
// 定义http返回错误信息的结构体
type ErrorResponse struct {Code int json:codeMessage string json:message
}
②根据业务逻辑为结构体赋值相应的错误信息
//这里为了简化函数不进行业务逻辑判断而直接返回错误信息
er : ErrorResponse{Code: 403,Message: 用户名不能为空,
}
③将错误信息序列化并写入到 http.ResponseWriter 中
// 设置响应头为JSON类型
w.Header().Set(Content-Type, application/json)// 设置响应状态码为400
w.WriteHeader(http.StatusBadRequest)// 将ErrorResponse转换为JSON并写入响应体
//json.NewEncoder(w).Encode(he)//将错误信息结构体序列化并返回
res, _ : json.Marshal(er)
w.Write(res) 代码示例
package mainimport (encoding/jsonnet/http
)// 定义http返回错误信息的结构体
type ErrorResponse struct {Code int json:codeMessage string json:message
}func Login(w http.ResponseWriter, r *http.Request) {//这里为了简化函数不进行业务逻辑判断而直接返回错误信息er : ErrorResponse{Code: 403,Message: 用户名不能为空,}// 设置响应头为JSON类型w.Header().Set(Content-Type, application/json)// 设置响应状态码为400w.WriteHeader(http.StatusBadRequest)// 将ErrorResponse转换为JSON并写入响应体//json.NewEncoder(w).Encode(he)//将错误信息结构体序列化并返回res, _ : json.Marshal(er)w.Write(res)
}func main() {//创建一个 HTTP 请求路由器mux : http.NewServeMux()mux.Handle(/login, http.HandlerFunc(Login))http.ListenAndServe(:8081, mux)
}
效果演示 二、微服务框架kratos响应错误的方式
Kratos官网有关错误处理的介绍错误处理 | Kratos
Kratos 有关错误处理的 examples 代码见examples/errors 、examples/http/errors
1、kratos默认的错误信息格式
kratos响应错误信息的默认JSON格式为:
{// 错误码跟 http-status 一致并且在 grpc 中可以转换成 grpc-statuscode: 500,// 错误原因定义为业务判定错误码reason: USER_NOT_FOUND,// 错误信息为用户可读的信息可作为用户提示内容message: invalid argument error,// 错误元信息为错误添加附加可扩展信息metadata: {foo: bar}
}
使用方法
①导入 kratos 的 errors 包
import github.com/go-kratos/kratos/v2/errors
②在业务逻辑中需要响应错误时用 New 方法生成错误信息或通过 proto 生成的代码响应错误
注意这里的 New 方法是 Kratos 框架中的 errros.New而不是 go 原生的 errors.New
func NewLoginRequest(username, password string) (*LoginRequest, error) {// 校验参数if username {//通过 New 方法创建一个错误信息err : errors.New(500, USER_NAME_EMPTY, 用户名不能为空)// 传递metadataerr err.WithMetadata(map[string]string{ remark: 请求参数中的 phone 字段为空,})return nil, err}if password {// 通过 proto 生成的代码响应错误并且包名应替换为自己生成代码后的 package name return nil, api.ErrorPasswordIsEmpty(密码不能为空)}return LoginRequest{username: username,password: password,}, nil
}
返回结果 2、自定义错误信息格式
如果不想使用 kratos 默认的错误响应格式可以自定义错误信息处理格式方法如下
①自定义错误信息结构体
type HTTPError struct {Code int json:codeMessage string json:messageMoreInfo string json:moreInfo
}
②实现 FromError、errorEncoder 等方法
func (e *HTTPError) Error() string {return fmt.Sprintf(HTTPError code: %d message: %s, e.Code, e.Message)
}// FromError try to convert an error to *HTTPError.
func FromError(err error) *HTTPError {if err nil {return nil}if se : new(HTTPError); errors.As(err, se) {return se}return HTTPError{Code: 500}
}func errorEncoder(w stdhttp.ResponseWriter, r *stdhttp.Request, err error) {se : FromError(err)codec, _ : http.CodecForRequest(r, Accept)body, err : codec.Marshal(se)if err ! nil {w.WriteHeader(500)return}w.Header().Set(Content-Type, application/codec.Name())w.WriteHeader(se.Code)_, _ w.Write(body)
}
③创建 http.Server 时使用函数 http.ErrorEncoder() 将上述 errorEncoder 添加到 ServerOption 中
httpSrv : http.NewServer(http.Address(:8000),http.ErrorEncoder(errorEncoder),
)
④业务逻辑中需要响应错误的地方返回自定义消息对象
return HTTPError{Code: 400, Message: 用户名不存在, MoreInfo: 请求参数中 userName 张三}
完整代码示例为
package mainimport (errorsfmtlogstdhttp net/httpgithub.com/go-kratos/kratos/v2github.com/go-kratos/kratos/v2/transport/http
)// HTTPError is an HTTP error.
type HTTPError struct {Code int json:codeMessage string json:messageMoreInfo string json:moreInfo
}func (e *HTTPError) Error() string {return fmt.Sprintf(HTTPError code: %d message: %s, e.Code, e.Message)
}// FromError try to convert an error to *HTTPError.
func FromError(err error) *HTTPError {if err nil {return nil}if se : new(HTTPError); errors.As(err, se) {return se}return HTTPError{Code: 500}
}func errorEncoder(w stdhttp.ResponseWriter, r *stdhttp.Request, err error) {se : FromError(err)codec, _ : http.CodecForRequest(r, Accept)body, err : codec.Marshal(se)if err ! nil {w.WriteHeader(500)return}w.Header().Set(Content-Type, application/codec.Name())w.WriteHeader(se.Code)_, _ w.Write(body)
}func main() {httpSrv : http.NewServer(http.Address(:8082),http.ErrorEncoder(errorEncoder),)router : httpSrv.Route(/)router.GET(login, func(ctx http.Context) error {return HTTPError{Code: 400, Message: 用户名不存在, MoreInfo: 请求参数中 userName 张三}})app : kratos.New(kratos.Name(mux),kratos.Server(httpSrv,),)if err : app.Run(); err ! nil {log.Fatal(err)}
}返回结果 3、kratos返回错误信息JSON的源码探究
至此了解了 go 原生 http 和微服务框架 kratos 响应错误信息的处理方式对比可发现
①在原生http响应处理中我们先将错误消息结构体序列化 res, _ : json.Marshal(er)然后通过 http.ResponseWriter.Write(res) 写入错误信息JSON并返回
②在 kratos 中我们在业务处理函数中仅仅通过 return errors.New() 返回了一个 error并没有将其序列化但整个http请求却返回了一个有关错误信息的 json 字符串
是什么原因呢原来是 kratos 框架内部完成了将错误信息结构体序列化并写入http.ResponseWriter的过程。
具体实现方式如下
http server 结构体 Server 中含有一个字段 ene EncodeErrorFunc专门用来进行错误处理
//http/server.go
// Server is an HTTP server wrapper.
type Server struct {*http.Serverlis net.ListenertlsConf *tls.Configendpoint *url.URLerr errornetwork stringaddress stringtimeout time.Durationfilters []FilterFuncmiddleware matcher.MatcherdecVars DecodeRequestFuncdecQuery DecodeRequestFuncdecBody DecodeRequestFuncenc EncodeResponseFuncene EncodeErrorFunc // 用于错误处理strictSlash boolrouter *mux.Router
}//http/codec.go
// EncodeErrorFunc is encode error func.
type EncodeErrorFunc func(http.ResponseWriter, *http.Request, error)
使用 NewServer() 创建时 http Server 时ene 属性会默认为 DefaultErrorEncoder该函数会将序列化错误信息并写入到 http.ResponseWriter 中
//http/server.go
// NewServer creates an HTTP server by options.
func NewServer(opts ...ServerOption) *Server {srv : Server{network: tcp,address: :0,timeout: 1 * time.Second,middleware: matcher.New(),decVars: DefaultRequestVars,decQuery: DefaultRequestQuery,decBody: DefaultRequestDecoder,enc: DefaultResponseEncoder,ene: DefaultErrorEncoder, //默认的错误处理函数strictSlash: true,router: mux.NewRouter(),}for _, o : range opts {o(srv)}srv.router.StrictSlash(srv.strictSlash)srv.router.NotFoundHandler http.DefaultServeMuxsrv.router.MethodNotAllowedHandler http.DefaultServeMuxsrv.router.Use(srv.filter())srv.Server http.Server{Handler: FilterChain(srv.filters...)(srv.router),TLSConfig: srv.tlsConf,}return srv
}//http/codec.go
// DefaultErrorEncoder encodes the error to the HTTP response.
func DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, err error) {se : errors.FromError(err)codec, _ : CodecForRequest(r, Accept)body, err : codec.Marshal(se) //序列化错误信息结构体if err ! nil {w.WriteHeader(http.StatusInternalServerError)return}w.Header().Set(Content-Type, httputil.ContentType(codec.Name()))w.WriteHeader(int(se.Code))_, _ w.Write(body) //将序列化的结果写入到响应中
}路由处理函数 router.GET() 等会调用 Router.Handle 其中会判断 HandlerFunc 是否有返回错误如果有则会调用 server.ene 函数从而完成错误信息序列化并返回
//main.go
func main() {httpSrv : http.NewServer(http.Address(:8082),)router : httpSrv.Route(/)router.GET(login, func(ctx http.Context) error {return errors.New(500, USER_NOT_FOUND, 用户名不存在)})
}//http/router.go
// GET registers a new GET route for a path with matching handler in the router.
func (r *Router) GET(path string, h HandlerFunc, m ...FilterFunc) {r.Handle(http.MethodGet, path, h, m...)
}//http/router.go
// Handle registers a new route with a matcher for the URL path and method.
func (r *Router) Handle(method, relativePath string, h HandlerFunc, filters ...FilterFunc) {next : http.Handler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {ctx : r.pool.Get().(Context)ctx.Reset(res, req)//重点这里判断路由处理函数是否返回了 error如果是则调用 server.ene 函数序列化错误信息并返回if err : h(ctx); err ! nil {r.srv.ene(res, req, err)}ctx.Reset(nil, nil)r.pool.Put(ctx)}))next FilterChain(filters...)(next)next FilterChain(r.filters...)(next)r.srv.router.Handle(path.Join(r.prefix, relativePath), next).Methods(method)
}
自定义错误消息结构体后创建 http server 时通过 http.ErrorEncoder(errorEncoder) 将自定义的错误处理函数赋值给 server.ene替换了默认的 DefaultErrorEncoder
// main.go
//自定义的错误处理函数
func errorEncoder(w stdhttp.ResponseWriter, r *stdhttp.Request, err error) {se : FromError(err)codec, _ : http.CodecForRequest(r, Accept)body, err : codec.Marshal(se)if err ! nil {w.WriteHeader(500)return}w.Header().Set(Content-Type, application/codec.Name())w.WriteHeader(se.Code)_, _ w.Write(body)
}// main.go
func main() {httpSrv : http.NewServer(http.Address(:8082),http.ErrorEncoder(errorEncoder), // 将自定义的错误处理函数赋值给 sever)
}// http/server.go
// ErrorEncoder with error encoder.
func ErrorEncoder(en EncodeErrorFunc) ServerOption {return func(o *Server) {o.ene en}
}