# 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
方法,用以手动进行匹配,该方法将在 Prefix
、 Path
等匹配后且没有匹配上再进入该方法匹配。
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 能正确处理。