# Filter 与 Middleware

# 跨域配置

http.Filter(handlers.CORS(
    handlers.AllowedHeaders([]string{"Content-Type", "X-Requested-With", "Authorization"}),
    handlers.AllowedOrigins([]string{"*"}),
    handlers.AllowedMethods([]string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"}),
))

# Middleware 匹配路由

通过使用 selector.Server 来增加对路由的匹配:

selector.Server(
    middleware.Auth,
).
    Prefix("/api.user.v1.User"). // 前缀
    Path("/api.user.v1.User/GetInfo"). // 具体路径
    Match(NewWhiteListMatcher). // 函数匹配
    Build()

NewWhiteListMatcher 返回一个 selector.MatchFunc 方法,用以手动进行匹配,该方法将在 PrefixPath 等匹配后且没有匹配上再进入该方法匹配。

func NewWhiteListMatcher() selector.MatchFunc {
    whiteList := make(map[string]struct{})
    whiteList[userV1.OperationGetInfo] = struct{}{}
    whiteList["/api.user.v1.User/Login"] = struct{}{}
    return func(ctx context.Context, operation string) bool {
        if _, ok := whiteList[operation]; ok {
            return false
        }
        return true
    }
}

# 返回值编码

使用 protobuf 定义 api 后,如果要返回统一的结构数据,如:

{
    "code": 1000,
    "message": "OK",
    "data": {}
}

需要设置 http.ResponseEncoder 选项,首先使用 protobuf 定义一个返回值类型:

message ApiResult {
    int32 code = 1;
    string message = 2;
    google.protobuf.Any data = 3;
}

设置 Encoder 方法时返回的 value 为 interface 类型,需要使用 ret, err := anypb.New(v.(proto.Message)) 来转换为 google.protobuf.Any 类型。

但这样转换后的数据再转为 JSON 后会带上 protobuf 的类型信息:

"@type": "type.googleapis.com/api.pano.v1.GetPanoReply"

所以此处的转换方法可这样:

import (
    "github.com/go-kratos/kratos/v2/encoding"
    nHttp "net/http"
    "github.com/tidwall/sjson"
)
func PrimaryResponseEncoder(w nHttp.ResponseWriter, _ *nHttp.Request, v interface{}) error {
	m := make(map[string]interface{})
	m["code"] = 200
	m["message"] = "OK"
	codec := encoding.GetCodec("json")
	mBytes, err := codec.Marshal(m)
	if err != nil {
		return err
	}
	jsonRes := string(mBytes)
	resBytes, err := codec.Marshal(v)
	if err != nil {
		return err
	}
	raw, err := sjson.SetRaw(jsonRes, "data", string(resBytes))
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", fmt.Sprintf("application/%s", codec.Name()))
	_, _ = w.Write([]byte(raw))
	return nil
}

此处借由 kratos 的 encoding 编码原数据,转换为 JSON 字符串后使用 sjson.SetRaw 的方式设置进定义的返回结构中。
如果直接使用 map["data"] 将值放入的方式转换返回,某些特性将失去,例如定义的 uint64、int64 等类型会自动转换为 string 类型,以确保 Javascript 能正确处理。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

HuaLin 微信支付

微信支付

HuaLin 支付宝

支付宝