Files
ginrpc/ginrpc.go
2023-03-10 16:40:30 +08:00

240 lines
5.6 KiB
Go

package ginrpc
import (
"reflect"
"runtime"
"strings"
"github.com/xxjwxc/public/myast"
"github.com/xxjwxc/public/mylog"
"github.com/gin-gonic/gin"
"github.com/xxjwxc/ginrpc/api"
"github.com/xxjwxc/public/errors"
)
// _Base base struct
type _Base struct {
isBigCamel bool // big camel style.大驼峰命名规则
isDev bool // if is development
apiFun NewAPIFunc
apiType reflect.Type
outPath string // output path.输出目录
beforeAfter []GinBeforeAfter
isOutDoc bool
recoverErrorFunc RecoverErrorFunc
}
// Option overrides behavior of Connect.
type Option interface {
apply(*_Base)
}
type optionFunc func(*_Base)
func (f optionFunc) apply(o *_Base) {
f(o)
}
// WithOutPath set output path dir with router.go file.设置输出目录
func WithOutPath(path string) Option {
return optionFunc(func(o *_Base) {
if !strings.HasSuffix(path, "/") {
path += "/"
}
o.outPath = path
})
}
// WithCtx use custom context.设置自定义context
func WithCtx(middleware NewAPIFunc) Option {
return optionFunc(func(o *_Base) {
o.Model(middleware)
})
}
// WithDebug set build is development.设置debug模式(默认debug模式)
func WithDebug(b bool) Option {
return optionFunc(func(o *_Base) {
o.Dev(b)
})
}
// WithImportFile 添加自定义import文件列表
func WithImportFile(k, v string) Option {
return optionFunc(func(o *_Base) {
myast.AddImportFile(k, v)
})
}
// WithBigCamel set build is BigCamel.是否大驼峰模式
func WithBigCamel(b bool) Option {
return optionFunc(func(o *_Base) {
o.isBigCamel = b
})
}
// WithOutDoc set is out doc.是否输出文档
func WithOutDoc(b bool) Option {
return optionFunc(func(o *_Base) {
o.isOutDoc = b
})
}
// WithBeforeAfter set before and after call.设置对象调用前后执行中间件
func WithBeforeAfter(beforeAfter GinBeforeAfter) Option {
return optionFunc(func(o *_Base) {
o.beforeAfter = append(o.beforeAfter, beforeAfter)
})
}
// Default new op obj
func Default() *_Base {
b := new(_Base)
b.Model(api.NewAPIFunc)
b.Dev(true)
b.SetRecover(func(err interface{}) {
mylog.Error(err)
})
return b
}
// New new customized base
func New(opts ...Option) *_Base {
b := Default() // default option
for _, o := range opts {
o.apply(b)
}
b.SetRecover(func(err interface{}) {
mylog.Error(err)
})
return b
}
// Dev set build is development
func (b *_Base) Dev(isDev bool) {
b.isDev = isDev
}
// SetRecover set recover err call
func (b *_Base) SetRecover(errfun func(interface{})) {
b.recoverErrorFunc = errfun
}
// OutDoc set if out doc. 设置是否输出接口文档
func (b *_Base) OutDoc(isOutDoc bool) {
b.isOutDoc = isOutDoc
}
// Model use custom context
func (b *_Base) Model(middleware NewAPIFunc) *_Base {
if middleware == nil { // default middleware
middleware = api.NewAPIFunc
}
b.apiFun = middleware // save callback
rt := reflect.TypeOf(middleware(&gin.Context{}))
if rt == nil || rt.Kind() != reflect.Ptr {
panic("need pointer")
}
b.apiType = rt
return b
}
// Register Registered by struct object,[prepath + bojname.]
func (b *_Base) Register(router gin.IRoutes, cList ...interface{}) bool {
if b.isDev {
b.tryGenRegister(router, cList...)
}
return b.register(router, cList...)
}
// RegisterHandlerFunc Multiple registration methods.获取并过滤要绑定的参数
func (b *_Base) RegisterHandlerFunc(router gin.IRoutes, httpMethod []string, relativePath string, handlerFuncs ...interface{}) error {
list := make([]gin.HandlerFunc, 0, len(handlerFuncs))
for _, call := range handlerFuncs {
list = append(list, b.HandlerFunc(call))
}
for _, v := range httpMethod {
// method := strings.ToUpper(v)
// switch method{
// case "ANY":
// router.Any(relativePath,list...)
// default:
// router.Handle(method,relativePath,list...)
// }
// or
switch strings.ToUpper(v) {
case "POST":
router.POST(relativePath, list...)
case "GET":
router.GET(relativePath, list...)
case "DELETE":
router.DELETE(relativePath, list...)
case "PATCH":
router.PATCH(relativePath, list...)
case "PUT":
router.PUT(relativePath, list...)
case "OPTIONS":
router.OPTIONS(relativePath, list...)
case "HEAD":
router.HEAD(relativePath, list...)
case "ANY":
router.Any(relativePath, list...)
default:
return errors.Errorf("method:[%v] not support", httpMethod)
}
}
return nil
}
// HandlerFunc Get and filter the parameters to be bound
func (b *_Base) HandlerFunc(handlerFunc interface{}) gin.HandlerFunc { // 获取并过滤要绑定的参数
typ := reflect.ValueOf(handlerFunc).Type()
if typ.NumIn() == 1 { // Parameter checking 参数检查
ctxType := typ.In(0)
// go-gin default method
if ctxType == reflect.TypeOf(&gin.Context{}) {
return handlerFunc.(func(*gin.Context))
}
// Customized context . 自定义的context
if ctxType == b.apiType {
method := reflect.ValueOf(handlerFunc)
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
b.recoverErrorFunc(err)
}
}()
method.Call([]reflect.Value{reflect.ValueOf(b.apiFun(c))})
}
}
panic("method " + runtime.FuncForPC(reflect.ValueOf(handlerFunc).Pointer()).Name() + " not support!")
}
// Custom context type with request parameters .自定义的context类型,带request 请求参数
call, err := b.getCallFunc3(reflect.ValueOf(handlerFunc))
if err != nil { // Direct reporting error.
panic(err)
}
return call
}
// CheckHandlerFunc Judge whether to match rules
func (b *_Base) CheckHandlerFunc(handlerFunc interface{}) (int, bool) { // 判断是否匹配规则,返回参数个数
typ := reflect.ValueOf(handlerFunc).Type()
return b.checkHandlerFunc(typ, false)
}