当前位置: 首页 > 工具软件 > Kratos > 使用案例 >

kratos V2 框架设计自定义返回结构以及跨域

宦书
2023-12-01

在Web开发业务中,通常使用业务状态码来判断业务状态,不使用http状态码。在kratos V2中控制 http 的返回值,在 http 返回值外面返回自己熟悉的结构

在NewHTTPServer中进行使用(替换返回结构)

// 过滤,所包含的路由不会经过jwt权限验证
func NewSkipRoutersMatcher() selector.MatchFunc {
	//完整 todo /包名.服务名/方法名
	smn := "/intelligent_analysis.v1.Analysis" //包名.服务名
	skipRouters := make(map[string]struct{})
	skipRouters[smn+"/Login"] = struct{}{} //通过operation匹配规则,并不是http本身的路由  /包名.服务名/方法名
	skipRouters[smn+"/Platform"] = struct{}{}
	return func(ctx context.Context, operation string) bool {
		if _, ok := skipRouters[operation]; ok {
			return false
		}
		return true
	}
}


/=================================================================
// NewHTTPServer new a HTTP server.
func NewHTTPServer(c *conf.Server, jwtc *conf.JWT, cloud_platform *service.AnalysisService, logger log.Logger) *http.Server {
	var opts = []http.ServerOption{
		//自定义返回错误结构
		http.ErrorEncoder(errorEncoder), //替代默认的错误结构
        //自定义返回数据结构
		http.ResponseEncoder(responseEncoder),  //替代默认的返回数据结构
		http.Middleware(
			recovery.Recovery(),
			selector.Server(auth.JWTAuth(jwtc.Secret)).Match(NewSkipRoutersMatcher()).Build(),
			logging.Server(logger),
        //cors.MiddlewareCors(), //跨域中间件
		),       
		http.Filter(
		  //跨域  推荐
			handlers.CORS(
				handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}),
				handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS", "DELETE"}),
				handlers.AllowedOrigins([]string{"*"}),
			)),
	}
	if c.Http.Network != "" {
		opts = append(opts, http.Network(c.Http.Network))
	}
	if c.Http.Addr != "" {
		opts = append(opts, http.Address(c.Http.Addr))
	}
	if c.Http.Timeout != nil {
		opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
	}
	srv := http.NewServer(opts...)
	v1.RegisterAnalysisHTTPServer(srv, cloud_platform)
	return srv
}

返回结构的具体实现如下:

type ResponseError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (e *ResponseError) Error() string {
	return fmt.Sprintf("ResponseError: %d", e.Code)
}

func NewResponseError(code int, message string) *ResponseError {
	return &ResponseError{
		Code:    code,
		Message: message,
	}
}

// FromError try to convert an error to *ResponseError.
func FromError(err error) *ResponseError {
	if err == nil {
		return nil
	}
	if se := new(errors.Error); errors.As(err, &se) {
		return NewResponseError(int(se.Code), se.Message)
	}
	return &ResponseError{Code: 500}
}

//自定义返回error
func errorEncoder(w nethttp.ResponseWriter, r *nethttp.Request, err error) {
	se := errors.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 responseEncoder(w http.ResponseWriter, r *http.Request, data interface{}) error {
	type Response struct {
		Code    int         `json:"code"`
		Data    interface{} `json:"data"`
		Message string      `json:"message"`
	}
///************************************
    //Code与Message直接写固定值
	res := &Response{
		  Code:    200,
		  Data:    data,
		  Message: "成功",
	}
///**********************************/
	//codec := encoding.GetCodec("json")  //这两行的目的就是为了把要返回的数据进行序列化
	//msRes, err := codec.Marshal(res)    //也可采用上面函数errorEncoder中的方法或者下面的直接JSON序列化
	msRes, err := json.Marshal(res)
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(msRes)
	return nil
}

跨域中间件

        前端需要将withCredentials设置为 false

// MiddlewareCors 设置跨域请求头
func MiddlewareCors() middleware.Middleware {
	return func(handler middleware.Handler) middleware.Handler {
		return func(ctx context.Context, req interface{}) (interface{}, error) {
			if tr, ok := transport.FromServerContext(ctx); ok {
                //tr.ReplyHeader().Set("Access-Control-Allow-Origin", "*")
				//tr.ReplyHeader().Set("Access-Control-Allow-Credentials", "true")
				//tr.ReplyHeader().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,PATCH,DELETE")
				//tr.ReplyHeader().Set("Access-Control-Allow-Headers", "Content-Type,X-Requested-With,Authorization")
				if ht, ok := tr.(http.Transporter); ok {
					origin := ht.RequestHeader().Get("Origin")
					method := ht.Request().Method
					if method == nethttp.MethodOptions {
						ht.ReplyHeader().Set("Access-Control-Allow-Origin", origin) //origin也可以替换为 *
						ht.ReplyHeader().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,PATCH,DELETE")
						ht.ReplyHeader().Set("Access-Control-Allow-Credentials", "true")
						ht.ReplyHeader().Set("Access-Control-Allow-Headers", "Content-Type, X-Requested-With,Authorization")
					}					
				}
			}
			return handler(ctx, req)
		}
	}
}

//func NotFoundHandler(res nethttp.ResponseWriter, req *nethttp.Request) {
//	res.Header().Set("Access-Control-Allow-Origin", "*")
//	res.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,PATCH,DELETE")
//	res.Header().Set("Access-Control-Allow-Credentials", "true")
//	res.Header().Set("Access-Control-Allow-Headers", "Content-Type,"+
//		"X-Requested-With,Access-Control-Allow-Credentials,User-Agent,Content-Length,Authorization")
//
//	log.Info("NotFoundHandler")
//
//	errCode := errors.NotFound("page not found", "page not found")
//	buffer, _ := json.Marshal(errCode)
//	//res.WriteHeader(400)
//	_, _ = res.Write(buffer)
//}

 类似资料: