rpc mw
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
package mw
|
||||
|
||||
import (
|
||||
"OpenIM/pkg/common/constant"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func CorsHandler() gin.HandlerFunc {
|
||||
return func(context *gin.Context) {
|
||||
context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
context.Header("Access-Control-Allow-Methods", "*")
|
||||
context.Header("Access-Control-Allow-Headers", "*")
|
||||
context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
|
||||
context.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒
|
||||
context.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true
|
||||
context.Header("content-type", "application/json") // 设置返回格式是json
|
||||
//Release all option pre-requests
|
||||
if context.Request.Method == http.MethodOptions {
|
||||
context.JSON(http.StatusOK, "Options Request!")
|
||||
}
|
||||
context.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func GinParseOperationID() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if c.Request.Method == http.MethodPost {
|
||||
operationID := c.Request.Header.Get(constant.OperationID)
|
||||
if operationID == "" {
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.String(400, "read request body error: "+err.Error())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
req := struct {
|
||||
OperationID string `json:"operationID"`
|
||||
}{}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
c.String(400, "get operationID error: "+err.Error())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if req.OperationID == "" {
|
||||
c.String(400, "operationID empty")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader(body))
|
||||
operationID = req.OperationID
|
||||
c.Request.Header.Set(constant.OperationID, operationID)
|
||||
}
|
||||
c.Set(constant.OperationID, operationID)
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package mw
|
||||
|
||||
import (
|
||||
"OpenIM/pkg/common/constant"
|
||||
"OpenIM/pkg/common/log"
|
||||
"OpenIM/pkg/common/tracelog"
|
||||
"OpenIM/pkg/errs"
|
||||
"context"
|
||||
"fmt"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func rpcServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||
var operationID string
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.NewError(operationID, info.FullMethod, "type:", fmt.Sprintf("%T", r), "panic:", r, "stack:", string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
funcName := info.FullMethod
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return nil, status.New(codes.InvalidArgument, "missing metadata").Err()
|
||||
}
|
||||
if opts := md.Get(constant.OperationID); len(opts) != 1 || opts[0] == "" {
|
||||
return nil, status.New(codes.InvalidArgument, "operationID error").Err()
|
||||
} else {
|
||||
operationID = opts[0]
|
||||
}
|
||||
var opUserID string
|
||||
if opts := md.Get(constant.OpUserID); len(opts) == 1 {
|
||||
opUserID = opts[0]
|
||||
}
|
||||
ctx = tracelog.NewRpcCtx(ctx, funcName, operationID)
|
||||
defer log.ShowLog(ctx)
|
||||
tracelog.SetCtxInfo(ctx, funcName, err, "opUserID", opUserID, "rpcReq", rpcString(req))
|
||||
resp, err = handler(ctx, req)
|
||||
if err != nil {
|
||||
tracelog.SetCtxInfo(ctx, funcName, err)
|
||||
return nil, rpcErrorToCode(err).Err()
|
||||
}
|
||||
tracelog.SetCtxInfo(ctx, funcName, nil, "rpcResp", rpcString(resp))
|
||||
return
|
||||
}
|
||||
|
||||
func rpcClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) {
|
||||
if ctx == nil {
|
||||
return errs.ErrInternalServer.Wrap("call rpc request context is nil")
|
||||
}
|
||||
operationID, ok := ctx.Value(constant.OperationID).(string)
|
||||
if !ok {
|
||||
return errs.ErrArgs.Wrap("ctx missing operationID")
|
||||
}
|
||||
md := metadata.Pairs(constant.OperationID, operationID)
|
||||
opUserID, ok := ctx.Value(constant.OpUserID).(string)
|
||||
if ok {
|
||||
md.Append(constant.OpUserID, opUserID)
|
||||
}
|
||||
err = invoker(metadata.NewOutgoingContext(ctx, md), method, req, reply, cc, opts...)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
rpcErr, ok := err.(interface{ GRPCStatus() *status.Status })
|
||||
if !ok {
|
||||
return errs.NewCodeError(errs.DefaultOtherError, err.Error()).Wrap()
|
||||
}
|
||||
sta := rpcErr.GRPCStatus()
|
||||
if sta.Code() == 0 {
|
||||
return errs.NewCodeError(errs.DefaultOtherError, err.Error()).Wrap()
|
||||
}
|
||||
details := sta.Details()
|
||||
if len(details) == 0 {
|
||||
return errs.NewCodeError(int(sta.Code()), sta.Message()).Wrap()
|
||||
}
|
||||
if v, ok := details[0].(*wrapperspb.StringValue); ok {
|
||||
return errs.NewCodeError(int(sta.Code()), sta.Message()).Wrap(v.String())
|
||||
}
|
||||
return errs.NewCodeError(int(sta.Code()), sta.Message()).Wrap()
|
||||
}
|
||||
|
||||
func GrpcServer() grpc.ServerOption {
|
||||
return grpc.UnaryInterceptor(rpcServerInterceptor)
|
||||
}
|
||||
|
||||
func GrpcClient() grpc.DialOption {
|
||||
return grpc.WithUnaryInterceptor(rpcClientInterceptor)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package mw
|
||||
|
||||
import (
|
||||
"OpenIM/pkg/errs"
|
||||
"fmt"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
"math"
|
||||
)
|
||||
|
||||
func rpcString(v interface{}) string {
|
||||
if s, ok := v.(interface{ String() string }); ok {
|
||||
return s.String()
|
||||
}
|
||||
return fmt.Sprintf("%+v", v)
|
||||
}
|
||||
|
||||
func rpcErrorToCode(err error) *status.Status {
|
||||
unwrap := errs.Unwrap(err)
|
||||
var (
|
||||
code codes.Code
|
||||
msg string
|
||||
)
|
||||
if unwrap.(errs.CodeError) != nil {
|
||||
c := unwrap.(errs.CodeError).Code()
|
||||
if c <= 0 || c > math.MaxUint32 {
|
||||
code = codes.OutOfRange // 错误码超出范围
|
||||
} else {
|
||||
code = codes.Code(c)
|
||||
}
|
||||
msg = unwrap.(errs.CodeError).Msg()
|
||||
} else {
|
||||
code = codes.Unknown
|
||||
msg = unwrap.Error()
|
||||
}
|
||||
sta := status.New(code, msg)
|
||||
if unwrap == err {
|
||||
return sta
|
||||
}
|
||||
details, err := sta.WithDetails(wrapperspb.String(fmt.Sprintf("%+v", err)))
|
||||
if err != nil {
|
||||
return sta
|
||||
}
|
||||
return details
|
||||
}
|
||||
Reference in New Issue
Block a user