feat: router register
This commit is contained in:
parent
0dd59eb0e9
commit
03a3aae71b
|
@ -0,0 +1,308 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.18.0
|
||||
// source: core.proto
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ErrMsg struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ErrCode int32 `protobuf:"varint,1,opt,name=err_code,json=errCode,proto3" json:"err_code,omitempty"`
|
||||
ErrMsg string `protobuf:"bytes,2,opt,name=err_msg,json=errMsg,proto3" json:"err_msg,omitempty"`
|
||||
Hint string `protobuf:"bytes,3,opt,name=hint,proto3" json:"hint,omitempty"`
|
||||
TraceId string `protobuf:"bytes,4,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"`
|
||||
//@gotags: json:"-"
|
||||
Autonomy bool `protobuf:"varint,5,opt,name=autonomy,proto3" json:"-"`
|
||||
}
|
||||
|
||||
func (x *ErrMsg) Reset() {
|
||||
*x = ErrMsg{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ErrMsg) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ErrMsg) ProtoMessage() {}
|
||||
|
||||
func (x *ErrMsg) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ErrMsg.ProtoReflect.Descriptor instead.
|
||||
func (*ErrMsg) Descriptor() ([]byte, []int) {
|
||||
return file_core_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ErrMsg) GetErrCode() int32 {
|
||||
if x != nil {
|
||||
return x.ErrCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ErrMsg) GetErrMsg() string {
|
||||
if x != nil {
|
||||
return x.ErrMsg
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ErrMsg) GetHint() string {
|
||||
if x != nil {
|
||||
return x.Hint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ErrMsg) GetTraceId() string {
|
||||
if x != nil {
|
||||
return x.TraceId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ErrMsg) GetAutonomy() bool {
|
||||
if x != nil {
|
||||
return x.Autonomy
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type TestStruct struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Child *TestChileStruct `protobuf:"bytes,1,opt,name=child,proto3" json:"child,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TestStruct) Reset() {
|
||||
*x = TestStruct{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TestStruct) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TestStruct) ProtoMessage() {}
|
||||
|
||||
func (x *TestStruct) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TestStruct.ProtoReflect.Descriptor instead.
|
||||
func (*TestStruct) Descriptor() ([]byte, []int) {
|
||||
return file_core_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *TestStruct) GetChild() *TestChileStruct {
|
||||
if x != nil {
|
||||
return x.Child
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestChileStruct struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Ping string `protobuf:"bytes,1,opt,name=ping,proto3" json:"ping,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TestChileStruct) Reset() {
|
||||
*x = TestChileStruct{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TestChileStruct) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TestChileStruct) ProtoMessage() {}
|
||||
|
||||
func (x *TestChileStruct) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TestChileStruct.ProtoReflect.Descriptor instead.
|
||||
func (*TestChileStruct) Descriptor() ([]byte, []int) {
|
||||
return file_core_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *TestChileStruct) GetPing() string {
|
||||
if x != nil {
|
||||
return x.Ping
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_core_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_core_proto_rawDesc = []byte{
|
||||
0x0a, 0x0a, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x22, 0x87, 0x01, 0x0a, 0x06, 0x45, 0x72, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x19, 0x0a,
|
||||
0x08, 0x65, 0x72, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||
0x07, 0x65, 0x72, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x72, 0x72, 0x5f,
|
||||
0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x4d, 0x73,
|
||||
0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x04, 0x68, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64,
|
||||
0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x79, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x79, 0x22, 0x39, 0x0a, 0x0a,
|
||||
0x54, 0x65, 0x73, 0x74, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x63, 0x68,
|
||||
0x69, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
|
||||
0x52, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x22, 0x25, 0x0a, 0x0f, 0x54, 0x65, 0x73, 0x74, 0x43,
|
||||
0x68, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x69,
|
||||
0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x42, 0x09,
|
||||
0x5a, 0x07, 0x2e, 0x2f, 0x3b, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_core_proto_rawDescOnce sync.Once
|
||||
file_core_proto_rawDescData = file_core_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_core_proto_rawDescGZIP() []byte {
|
||||
file_core_proto_rawDescOnce.Do(func() {
|
||||
file_core_proto_rawDescData = protoimpl.X.CompressGZIP(file_core_proto_rawDescData)
|
||||
})
|
||||
return file_core_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_core_proto_goTypes = []interface{}{
|
||||
(*ErrMsg)(nil), // 0: core.ErrMsg
|
||||
(*TestStruct)(nil), // 1: core.TestStruct
|
||||
(*TestChileStruct)(nil), // 2: core.TestChileStruct
|
||||
}
|
||||
var file_core_proto_depIdxs = []int32{
|
||||
2, // 0: core.TestStruct.child:type_name -> core.TestChileStruct
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_core_proto_init() }
|
||||
func file_core_proto_init() {
|
||||
if File_core_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_core_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ErrMsg); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_core_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TestStruct); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_core_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TestChileStruct); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_core_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_core_proto_goTypes,
|
||||
DependencyIndexes: file_core_proto_depIdxs,
|
||||
MessageInfos: file_core_proto_msgTypes,
|
||||
}.Build()
|
||||
File_core_proto = out.File
|
||||
file_core_proto_rawDesc = nil
|
||||
file_core_proto_goTypes = nil
|
||||
file_core_proto_depIdxs = nil
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package core;
|
||||
|
||||
|
||||
option go_package = "./;core";
|
||||
|
||||
message ErrMsg {
|
||||
int32 err_code = 1;
|
||||
string err_msg = 2;
|
||||
string hint = 3;
|
||||
string trace_id = 4;
|
||||
// @json: -
|
||||
bool autonomy = 5;
|
||||
}
|
||||
|
||||
message TestStruct {
|
||||
TestChileStruct child = 1;
|
||||
}
|
||||
|
||||
message TestChileStruct {
|
||||
string ping = 1;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package core
|
||||
|
||||
import "fmt"
|
||||
|
||||
const UnknownError = "unknown"
|
||||
|
||||
const (
|
||||
// ErrNil 正常
|
||||
ErrNil = 0
|
||||
// ErrSystemError 系统错误
|
||||
ErrSystemError = -1
|
||||
// ErrProcessPanic 服务PANIC
|
||||
ErrProcessPanic = -10
|
||||
)
|
||||
|
||||
const (
|
||||
// ErrInvalidArg 请求参数无效
|
||||
ErrInvalidArg = 1001
|
||||
// ErrRecordNotFound 找不到记录
|
||||
ErrRecordNotFound = 1002
|
||||
// ErrConnectTimeout 连接超时
|
||||
ErrConnectTimeout = 1003
|
||||
// ErrFreqLimit 频率限制 [业务级]
|
||||
ErrFreqLimit = 1004
|
||||
// ErrRequestBroken 请求熔断
|
||||
ErrRequestBroken = 1005
|
||||
// ErrRequestRateLimit 请求限流 [服务级]
|
||||
ErrRequestRateLimit = 1006
|
||||
// ErrParamEmpty 请求参数为空
|
||||
ErrParamEmpty = 1007
|
||||
)
|
||||
|
||||
var (
|
||||
errCodeMap = map[int32]string{
|
||||
ErrNil: "ok",
|
||||
ErrSystemError: "system error",
|
||||
ErrProcessPanic: "process panic",
|
||||
ErrInvalidArg: "invalid arg",
|
||||
ErrRecordNotFound: "record not found",
|
||||
ErrConnectTimeout: "connect timeout",
|
||||
ErrFreqLimit: "request freq limit",
|
||||
ErrRequestBroken: "request is broken",
|
||||
ErrRequestRateLimit: "request rate is limited",
|
||||
ErrParamEmpty: "request param is empty",
|
||||
}
|
||||
)
|
||||
|
||||
func (m *ErrMsg) Error() string {
|
||||
return fmt.Sprintf("err_code: %d, err_msg: %s", m.ErrCode, m.ErrMsg)
|
||||
}
|
||||
|
||||
func RegisterError(m map[int32]string) {
|
||||
for k, v := range m {
|
||||
errCodeMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// GetErrMsg 基于错误码返回错误信息
|
||||
func GetErrMsg(errCode int32) string {
|
||||
if errCode == 0 {
|
||||
return "success"
|
||||
}
|
||||
|
||||
msg, ok := errCodeMap[errCode]
|
||||
if ok {
|
||||
return msg
|
||||
}
|
||||
if errCode < 0 {
|
||||
return "system error"
|
||||
}
|
||||
|
||||
return UnknownError
|
||||
}
|
||||
|
||||
// CreateError 基于错误码返回错误信息
|
||||
func CreateError(errCode int32) *ErrMsg {
|
||||
return &ErrMsg{ErrCode: errCode, ErrMsg: errCodeMap[errCode]}
|
||||
}
|
||||
|
||||
// GetErrCode 获取错误码 默认1 业务错误
|
||||
func GetErrCode(err error) int {
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if p, ok := err.(*ErrMsg); ok {
|
||||
return int(p.ErrCode)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
// CreateErrorWithMsg 自定义创建错误 如果错误码已提前注册 errMsg将失效
|
||||
func CreateErrorWithMsg(errCode int32, errMsg string) *ErrMsg {
|
||||
return &ErrMsg{ErrCode: errCode, ErrMsg: errMsg, Autonomy: true}
|
||||
}
|
|
@ -1,15 +1,20 @@
|
|||
package core
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type RouterNode struct {
|
||||
API string
|
||||
Method string
|
||||
Author string
|
||||
Describe string
|
||||
API string
|
||||
Method string
|
||||
Author string
|
||||
Describe string
|
||||
Middlewares []gin.HandlerFunc // 单一路由中间件组
|
||||
}
|
||||
|
||||
type RouterMap map[string]*RouterNode
|
||||
|
||||
type Routers struct {
|
||||
BaseURL string
|
||||
Apis RouterMap
|
||||
StructName string
|
||||
BaseURL string
|
||||
Apis RouterMap
|
||||
Middlewares []gin.HandlerFunc // 路由组统一中间件
|
||||
}
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gorilla/schema"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
ErrCode int `json:"err_code"`
|
||||
ErrMsg string `json:"err_msg"`
|
||||
Hint string `json:"hint,omitempty"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// Register 是一个组路由注射器
|
||||
// 只能通过NewRegister()进行实例化 否则会panic
|
||||
type Register struct {
|
||||
// GET参数解析器
|
||||
queryDecoder *schema.Decoder
|
||||
}
|
||||
|
||||
// NewRegister 实例化注册器
|
||||
func NewRegister() *Register {
|
||||
var register = &Register{queryDecoder: schema.NewDecoder()}
|
||||
register.queryDecoder.IgnoreUnknownKeys(true)
|
||||
register.queryDecoder.SetAliasTag("json")
|
||||
return register
|
||||
}
|
||||
|
||||
// RegisterStruct 按照 struct 的方法进行路由注册
|
||||
// rout: gin路由 建议传入group 将公共的中间件传递入group中
|
||||
// routers: 绑定自动生成的路由配置文件 proto同级的 autogen_router_module.go 文件中的 AutoGenXXXMap
|
||||
// igs: 需要注册的API组的struct ptr
|
||||
// 对于错误或异常零容忍 直接panic
|
||||
func (r *Register) RegisterStruct(rout gin.IRouter, routers Routers, igs ...interface{}) {
|
||||
if len(routers.Apis) == 0 {
|
||||
logrus.Warnf("%s api list empty, skip registe", routers.StructName)
|
||||
return
|
||||
}
|
||||
|
||||
if len(igs) == 0 {
|
||||
panic("group struct empty")
|
||||
}
|
||||
|
||||
for _, ig := range igs {
|
||||
//bind := ig.(BindGroupRouteSrv) // 你需要实现 BindGroupRouteSrv
|
||||
// 输出一下 rg
|
||||
refVal := reflect.ValueOf(ig)
|
||||
refTyp := reflect.TypeOf(ig)
|
||||
|
||||
//routConfig := r.routers[bind.Bind()]
|
||||
//if routConfig == nil {
|
||||
// panic("no func to register")
|
||||
//}
|
||||
//if routConfig.Apis == nil {
|
||||
// panic("no func to register")
|
||||
//}
|
||||
// 注册路由公共中间件
|
||||
//if len(routConfig.Middlewares) != 0 {
|
||||
// r.registerMiddleware(rout, routConfig.Middlewares)
|
||||
//}
|
||||
routMap := routers.Apis
|
||||
for m := 0; m < refTyp.NumMethod(); m++ {
|
||||
// 这里取出方法
|
||||
method := refTyp.Method(m)
|
||||
|
||||
var node *RouterNode
|
||||
var exist bool
|
||||
if node, exist = routMap[method.Name]; !exist {
|
||||
continue
|
||||
}
|
||||
if routers.BaseURL != "" {
|
||||
node.API = routers.BaseURL + node.API
|
||||
}
|
||||
// 注册路由
|
||||
if err := r.registerHandle(rout, node, method.Func, refVal); err != nil {
|
||||
logrus.Errorf("err: %+v", err)
|
||||
panic("err: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// registerMiddleware 注册中间件
|
||||
func (r *Register) registerMiddleware(router gin.IRouter, mws []gin.HandlerFunc) {
|
||||
router.Use(mws...)
|
||||
}
|
||||
|
||||
// registerHandle 注册Handle
|
||||
func (r *Register) registerHandle(router gin.IRouter, rc *RouterNode, rFunc, rGroup reflect.Value) error {
|
||||
call, err := r.getCallFunc(rFunc, rGroup)
|
||||
if err != nil {
|
||||
logrus.Errorf("get call err: %+v", err)
|
||||
return err
|
||||
}
|
||||
if call == nil {
|
||||
logrus.Warnf("get call func nil")
|
||||
return nil
|
||||
}
|
||||
|
||||
var hfs []gin.HandlerFunc
|
||||
if len(rc.Middlewares) != 0 {
|
||||
hfs = append(hfs, rc.Middlewares...)
|
||||
hfs = append(hfs, call)
|
||||
} else {
|
||||
hfs = append(hfs, call)
|
||||
}
|
||||
|
||||
switch rc.Method {
|
||||
case http.MethodPost:
|
||||
router.POST(rc.API, hfs...)
|
||||
case http.MethodGet:
|
||||
router.GET(rc.API, hfs...)
|
||||
case http.MethodDelete:
|
||||
router.DELETE(rc.API, hfs...)
|
||||
case http.MethodPatch:
|
||||
router.PATCH(rc.API, hfs...)
|
||||
case http.MethodPut:
|
||||
router.PUT(rc.API, hfs...)
|
||||
case http.MethodOptions:
|
||||
router.OPTIONS(rc.API, hfs...)
|
||||
case http.MethodHead:
|
||||
router.HEAD(rc.API, hfs...)
|
||||
case "ANY":
|
||||
router.Any(rc.API, hfs...)
|
||||
default:
|
||||
return fmt.Errorf("method:[%v] not support", rc.Method)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCallFunc 获取运行函数入口
|
||||
func (r *Register) getCallFunc(rFunc, rGroup reflect.Value) (gin.HandlerFunc, error) {
|
||||
typ := rFunc.Type() // 获取函数的类型
|
||||
|
||||
// 参数检查
|
||||
if typ.NumIn() != 3 {
|
||||
return nil, fmt.Errorf("func need two request param, (ctx, req)")
|
||||
}
|
||||
|
||||
// 响应检查
|
||||
if typ.NumOut() != 2 {
|
||||
return nil, fmt.Errorf("func need two response param, (resp, error)")
|
||||
}
|
||||
|
||||
// 第二返回参数是否是error
|
||||
if returnType := typ.Out(1); returnType != reflect.TypeOf((*error)(nil)).Elem() {
|
||||
return nil, errors.Errorf("method : %v , returns[1] %v not error",
|
||||
runtime.FuncForPC(rFunc.Pointer()).Name(), returnType.String())
|
||||
}
|
||||
|
||||
ctxType, reqType := typ.In(1), typ.In(2)
|
||||
if ctxType != reflect.TypeOf(&Context{}) {
|
||||
return nil, fmt.Errorf("first param must *core.Context")
|
||||
}
|
||||
|
||||
if reqType.Kind() != reflect.Ptr {
|
||||
return nil, fmt.Errorf("req type not ptr")
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logrus.Errorf("err: %+v\nstack: %+v", err, string(debug.Stack()))
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
req := reflect.New(reqType.Elem())
|
||||
// 参数校验
|
||||
err := r.bindAndValidate(c, req.Interface())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, struct {
|
||||
ErrCode int `json:"err_code"`
|
||||
ErrMsg string `json:"err_msg"`
|
||||
}{
|
||||
ErrCode: ErrInvalidArg,
|
||||
ErrMsg: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var returnValues = rFunc.Call([]reflect.Value{rGroup, reflect.ValueOf(&Context{Context: c}), req})
|
||||
|
||||
// 重定向的情况
|
||||
if c.Writer.Status() == http.StatusFound || c.Writer.Status() == http.StatusMovedPermanently {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 传输文件直接下载的情况
|
||||
ct := c.Writer.Header().Get("Content-Type")
|
||||
if ct == "application/octet-stream" {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if returnValues != nil {
|
||||
resp := returnValues[0].Interface()
|
||||
rerr := returnValues[1].Interface()
|
||||
|
||||
if rerr == nil {
|
||||
c.Writer.WriteHeader(http.StatusOK)
|
||||
respData, _ := jsoniter.Marshal(Result{
|
||||
ErrCode: ErrNil,
|
||||
ErrMsg: "ok",
|
||||
Data: resp,
|
||||
})
|
||||
_, _ = c.Writer.Write(respData)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
var errCode int
|
||||
var errMsg string
|
||||
|
||||
var isAutonomy bool
|
||||
if reflect.TypeOf(rerr).String() == "*core.ErrMsg" {
|
||||
e := rerr.(*ErrMsg)
|
||||
if e.Autonomy {
|
||||
err = e
|
||||
errCode = int(e.ErrCode)
|
||||
errMsg = e.ErrMsg
|
||||
isAutonomy = true
|
||||
}
|
||||
}
|
||||
|
||||
if !isAutonomy {
|
||||
err = rerr.(error)
|
||||
errCode = GetErrCode(err)
|
||||
errMsg = GetErrMsg(int32(errCode))
|
||||
}
|
||||
|
||||
c.Writer.WriteHeader(http.StatusOK)
|
||||
respData, _ := jsoniter.Marshal(Result{
|
||||
ErrCode: errCode,
|
||||
ErrMsg: errMsg,
|
||||
Data: resp,
|
||||
})
|
||||
_, _ = c.Writer.Write(respData)
|
||||
return
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
// bindAndValidate 绑定并校验参数
|
||||
func (r *Register) bindAndValidate(c *gin.Context, req interface{}) error {
|
||||
bodyBytes, _ := ioutil.ReadAll(c.Request.Body)
|
||||
c.Request.Body.Close()
|
||||
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
|
||||
// 校验
|
||||
if c.Request.Method == http.MethodGet {
|
||||
err := r.queryDecoder.Decode(req, c.Request.URL.Query())
|
||||
if err != nil {
|
||||
logrus.Errorf("unmarshal err: %+v", err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// 对于POST的非json传递
|
||||
if !strings.Contains(c.ContentType(), "application/json") {
|
||||
return nil
|
||||
}
|
||||
if len(bodyBytes) == 0 {
|
||||
bodyBytes = []byte("{}")
|
||||
}
|
||||
err := json.Unmarshal(bodyBytes, req)
|
||||
if err != nil {
|
||||
logrus.Errorf("unmarshal err: %+v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var validate = validator.New()
|
||||
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||
name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
|
||||
if name == "" {
|
||||
return field.Name
|
||||
}
|
||||
if name == "-" {
|
||||
return ""
|
||||
}
|
||||
return name
|
||||
})
|
||||
|
||||
err := validate.Struct(req)
|
||||
if err != nil {
|
||||
if _, ok := err.(*validator.InvalidValidationError); ok {
|
||||
logrus.Errorf("validate err: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, val := range err.(validator.ValidationErrors) {
|
||||
return fmt.Errorf("invalid request, field %s, rule %s", val.Field(), val.Tag())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type A struct {
|
||||
}
|
||||
|
||||
func (a *A) RetErr() error {
|
||||
var err error
|
||||
return err
|
||||
}
|
||||
|
||||
func TestRegister_getCallFunc(t *testing.T) {
|
||||
var a = new(A)
|
||||
fvo := reflect.ValueOf(a)
|
||||
ftp := fvo.Type()
|
||||
ff := ftp.Method(0).Func
|
||||
|
||||
fret := ff.Call([]reflect.Value{fvo})
|
||||
fmt.Println(fret[0].Interface() == nil)
|
||||
}
|
||||
|
||||
func TestNewRegister(t *testing.T) {
|
||||
//var reg = NewRegister()
|
||||
//reg.registerHandle()
|
||||
}
|
13
go.mod
13
go.mod
|
@ -2,7 +2,15 @@ module gitter.top/coco/coco
|
|||
|
||||
go 1.19
|
||||
|
||||
require github.com/gin-gonic/gin v1.9.0
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/go-playground/validator/v10 v10.11.2
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.8.0 // indirect
|
||||
|
@ -10,9 +18,7 @@ require (
|
|||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||
github.com/goccy/go-json v0.10.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
|
@ -26,6 +32,5 @@ require (
|
|||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue