You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
7.9 KiB
235 lines
7.9 KiB
package services
|
|
|
|
import (
|
|
"context"
|
|
"yunyan/comm"
|
|
"yunyan/pb"
|
|
"fmt"
|
|
"reflect"
|
|
"time"
|
|
|
|
jsoniter "github.com/json-iterator/go"
|
|
|
|
"yunyan/lego/core"
|
|
"yunyan/lego/core/cbase"
|
|
"yunyan/lego/sys/log"
|
|
"yunyan/lego/sys/pools"
|
|
)
|
|
|
|
// 加密信号:业务服务仅上报“该接口需要加密”,具体加密动作由网关模块执行。
|
|
// 信号通过 Rpc_GatewayHttpRouteResp.Encrypted 字段传递。
|
|
|
|
var json = jsoniter.Config{
|
|
EscapeHTML: true,
|
|
SortMapKeys: true,
|
|
ValidateJsonRawMessage: true,
|
|
ObjectFieldMustBeSimpleString: false,
|
|
CaseSensitive: false,
|
|
UseNumber: true, // 允许字符串转数字的预处理
|
|
DisallowUnknownFields: false,
|
|
}.Froze()
|
|
|
|
/*
|
|
服务网关组件 用于接收网关服务发送过来的消息
|
|
*/
|
|
|
|
// NewHttpRouteComp 创建路由组件。业务服务不会在这里做加密,只会根据业务声明的 EncryptMsgs
|
|
// 在响应中携带“需要加密”信号,由网关模块统一加密。
|
|
func NewHttpRouteComp() comm.ISC_HttpRouteComp {
|
|
comp := new(SCompHttpRoute)
|
|
return comp
|
|
}
|
|
|
|
// 服务网关组件
|
|
type SCompHttpRoute struct {
|
|
cbase.ServiceCompBase
|
|
|
|
service comm.IService //rpc服务对象 通过这个对象可以发布服务和调用其他服务的接口
|
|
msghandles map[string]*msghandle //处理函数的管理对象
|
|
interceptors []comm.ApiInterceptor //API 请求拦截器链,由业务模块注册
|
|
auditHook []comm.ApiAuditFunc //API 审计回调钩子,由业务模块注册
|
|
}
|
|
|
|
// AddInterceptor 添加 API 请求拦截器(在 handler 执行前调用)
|
|
func (this *SCompHttpRoute) AddInterceptor(interceptor comm.ApiInterceptor) {
|
|
this.interceptors = append(this.interceptors, interceptor)
|
|
}
|
|
|
|
// AddApiAuditHook 添加 API 审计回调钩子
|
|
func (this *SCompHttpRoute) AddApiAuditHook(hook comm.ApiAuditFunc) {
|
|
this.auditHook = append(this.auditHook, hook)
|
|
}
|
|
|
|
// 设置服务组件名称 方便业务模块中获取此组件对象
|
|
func (this *SCompHttpRoute) GetName() core.S_Comps {
|
|
return comm.SC_ServiceHttpRouteComp
|
|
}
|
|
|
|
// 组件初始化函数
|
|
func (this *SCompHttpRoute) Init(service core.IService, comp core.IServiceComp, options core.ICompOptions) (err error) {
|
|
err = this.ServiceCompBase.Init(service, comp, options)
|
|
this.service = service.(comm.IService)
|
|
this.msghandles = make(map[string]*msghandle)
|
|
pools.InitTypes(httpResultTyoe)
|
|
return err
|
|
}
|
|
|
|
// 组件启动时注册rpc服务监听
|
|
func (this *SCompHttpRoute) Start() (err error) {
|
|
this.service.Register(string(comm.Rpc_GatewayHttpRoute), this.Rpc_GatewayHttpRoute) //注册网关路由接收接口
|
|
err = this.ServiceCompBase.Start()
|
|
return
|
|
}
|
|
|
|
// 业务模块注册用户消息处理路由
|
|
func (this *SCompHttpRoute) RegisterRoute(methodName string, isEncrypt bool, comp reflect.Value, msg reflect.Type, handele reflect.Method) {
|
|
log.Debugf("注册用户路由【%s】", methodName)
|
|
_, ok := this.msghandles[methodName]
|
|
if ok {
|
|
log.Errorf("重复 注册网关消息【%s】", methodName)
|
|
return
|
|
}
|
|
this.msghandles[methodName] = &msghandle{
|
|
rcvr: comp,
|
|
msgType: msg,
|
|
handle: handele,
|
|
encrypt: isEncrypt,
|
|
}
|
|
//注册类型池
|
|
pools.InitTypes(msg)
|
|
}
|
|
|
|
// Rpc_GatewayRoute服务接口的接收函数
|
|
func (this *SCompHttpRoute) Rpc_GatewayHttpRoute(ctx context.Context, args *pb.Rpc_GatewayHttpRouteReq, reply *pb.Rpc_GatewayHttpRouteResp) (err error) {
|
|
var (
|
|
msghandle *msghandle
|
|
session comm.IUserSession
|
|
msg interface{}
|
|
httpResult *comm.HttpResult = pools.GetForType(httpResultTyoe).(*comm.HttpResult)
|
|
errordata *pb.ErrorData
|
|
ok bool
|
|
)
|
|
defer func() {
|
|
pools.PutForType(httpResultTyoe, httpResult)
|
|
if session != nil {
|
|
this.service.PutUserSession(session)
|
|
}
|
|
}()
|
|
msghandle, ok = this.msghandles[args.MsgName]
|
|
if ok {
|
|
session = this.service.GetUserSession(ctx, args.Meta)
|
|
stime := time.Now()
|
|
var auditCode int32 = 0
|
|
|
|
//序列化用户消息对象
|
|
msg = pools.GetForType(msghandle.msgType)
|
|
if len(args.Message) > 0 {
|
|
if err = json.Unmarshal(args.Message, msg); err != nil {
|
|
log.Errorf("[Handle Api] UserMessage:%s Unmarshal err:%v", args.MsgName, err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// 执行拦截器链
|
|
for _, interceptor := range this.interceptors {
|
|
if errData := interceptor(args.MsgName, session, msg); errData != nil {
|
|
httpResult.Code = errData.Code
|
|
httpResult.Message = errData.Message
|
|
httpResult.Data = nil
|
|
reply.ContentType = "application/json"
|
|
reply.Body, err = json.Marshal(httpResult)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
//执行处理流
|
|
handlereturn := msghandle.handle.Func.Call([]reflect.Value{msghandle.rcvr, reflect.ValueOf(session), reflect.ValueOf(msg)})
|
|
errdata := handlereturn[1]
|
|
if !errdata.IsNil() { //处理返货错误码 返回用户错误信息
|
|
errordata = errdata.Interface().(*pb.ErrorData)
|
|
httpResult.Code = errordata.Code
|
|
httpResult.Message = errordata.Message
|
|
httpResult.Data = nil
|
|
reply.ContentType = "application/json"
|
|
reply.Body, err = json.Marshal(httpResult)
|
|
auditCode = int32(errordata.Code)
|
|
log.Error("[Handle Api]",
|
|
log.Field{Key: "t", Value: time.Since(stime).Milliseconds()},
|
|
log.Field{Key: "m", Value: args.MsgName},
|
|
log.Field{Key: "meta", Value: args.Meta},
|
|
log.Field{Key: "req", Value: msg},
|
|
log.Field{Key: "reply", Value: httpResult},
|
|
)
|
|
} else {
|
|
returnVal := handlereturn[0]
|
|
returnType := returnVal.Type()
|
|
|
|
if returnType.Implements(typeOfMessage) {
|
|
httpResult.Code = pb.ErrorCode_Success
|
|
httpResult.Message = "Success"
|
|
httpResult.Data = handlereturn[0].Interface()
|
|
log.Debug("[Handle Api]",
|
|
log.Field{Key: "t", Value: time.Since(stime).Milliseconds()},
|
|
log.Field{Key: "m", Value: args.MsgName},
|
|
log.Field{Key: "meta", Value: args.Meta},
|
|
log.Field{Key: "req", Value: msg},
|
|
// log.Field{Key: "reply", Value: httpResult},
|
|
)
|
|
reply.ContentType = "application/json"
|
|
reply.Body, err = json.Marshal(httpResult)
|
|
} else if returnType == typeOfByteSlice {
|
|
log.Debug("[Handle Api]",
|
|
log.Field{Key: "t", Value: time.Since(stime).Milliseconds()},
|
|
log.Field{Key: "m", Value: args.MsgName},
|
|
log.Field{Key: "meta", Value: args.Meta},
|
|
log.Field{Key: "req", Value: msg},
|
|
// log.Field{Key: "reply_bytes", Value: len(returnVal.Bytes())},
|
|
)
|
|
if ct := session.GetMateToString("content_type"); ct != "" {
|
|
reply.ContentType = ct
|
|
} else {
|
|
reply.ContentType = "application/json"
|
|
}
|
|
reply.Body = returnVal.Bytes()
|
|
}
|
|
}
|
|
|
|
// 异步通知审计钩子(避免阻塞主请求流程)—— 在加密信号附加之前 clone reply.Body,仅记录明文供审计使用
|
|
if len(this.auditHook) > 0 {
|
|
auditSession := session.Clone()
|
|
reqBody := make([]byte, len(args.Message))
|
|
copy(reqBody, args.Message)
|
|
respBody := make([]byte, len(reply.Body))
|
|
copy(respBody, reply.Body)
|
|
costMs := time.Since(stime).Milliseconds()
|
|
msgName := args.MsgName
|
|
hooks := this.auditHook
|
|
go func() {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
log.Errorf("[AuditHook] panic: %v, api: %s", r, msgName)
|
|
}
|
|
}()
|
|
for _, v := range hooks {
|
|
v(msgName, auditSession, reqBody, respBody, auditCode, costMs)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// ===== 加密信号上报 =====
|
|
// 业务服务不执行加密动作,只在 reply 中标记 Encrypted=true,由网关模块根据配置的 key 统一加密。
|
|
// 仅对 application/json 响应标记加密,裸 bytes 文件流不参与加密。
|
|
if msghandle.encrypt && len(reply.Body) > 0 && reply.ContentType == "application/json" {
|
|
reply.Encrypted = true
|
|
}
|
|
} else { //未找到消息处理函数
|
|
log.Errorf("[Handle Api] no found handle %s", args.MsgName)
|
|
httpResult.Code = pb.ErrorCode_NoFindServiceHandleFunc
|
|
httpResult.Message = fmt.Sprintf("[Handle Http] no found handle %s", args.MsgName)
|
|
httpResult.Data = nil
|
|
reply.ContentType = "application/json"
|
|
reply.Body, err = json.Marshal(httpResult)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|