feat: router register

This commit is contained in:
Young Xu 2023-03-13 00:16:52 +08:00
parent 0dd59eb0e9
commit 03a3aae71b
Signed by: xuthus5
GPG Key ID: A23CF9620CBB55F9
7 changed files with 791 additions and 10 deletions

308
core/core.pb.go Normal file
View File

@ -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
}

23
core/core.proto Normal file
View File

@ -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;
}

96
core/errcode.go Normal file
View File

@ -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}
}

View File

@ -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 // 路由组统一中间件
}

314
core/router_register.go Normal file
View File

@ -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
}

View File

@ -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
View File

@ -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
)