From f978fa3f87b7500f1966c921f21ace6fec69a365 Mon Sep 17 00:00:00 2001 From: bryanqiu Date: Tue, 6 Dec 2022 19:15:35 +0800 Subject: [PATCH] add bindoutput & rewrite shapeoutput --- api.go | 40 ++++++++++++++++++++++++++-- context.go | 51 ++++++++++++++++++++++++++++++++++++ go.mod | 2 +- routes.go | 76 +++++++++++++++++++++++++----------------------------- 4 files changed, 125 insertions(+), 44 deletions(-) diff --git a/api.go b/api.go index 6c1631c..0288c45 100644 --- a/api.go +++ b/api.go @@ -1,6 +1,9 @@ package api import ( + "fmt" + "reflect" + "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "qoobing.com/gomod/log" @@ -10,6 +13,7 @@ import ( var ( validate = validator.New() requestLogidGetter = defaultLogidGetter + handlerMapper = map[string]Handler{} errCodeUnknownError = -10000 errCodeUnimplementApi = -20401 ) @@ -40,8 +44,27 @@ func NewEngine() *Engine { return &Engine{e} } -func (e *Engine) POST(uri string, handler HandlerFunc) { - e.Engine.POST(uri, handlerWrapper(handler)) +func (e *Engine) POST(uri string, handler Handler) { + h := handlerWrapper(handler.HandlerFunc()) + handlerMapper[fmt.Sprintf("%v", h)] = handler + e.Engine.POST(uri, h) +} + +// handlerWrapper +func handlerWrapper(handler HandlerFunc) gin.HandlerFunc { + return func(c *gin.Context) { + if icc, ok := c.Get("cc"); !ok { + log.Errorf("Unreachable Code: can not get cc(*api.Context) from *gin.Context") + } else if cc, ok := icc.(*Context); !ok { + log.Debugf("Unreachable Code: cc from *gin.Context is type of:[%s]", reflect.TypeOf(icc).String()) + log.Errorf("Unreachable Code: cc from *gin.Context is not type of *api.Context") + } else { + defer cc.TryRecover() + cc.AppId = c.GetString("appid") + cc.UserId = c.GetUint64("userid") + handler(cc) + } + } } // middlewareMyApiEngine myapi engine middleware @@ -72,6 +95,19 @@ func defaultLogidGetter(c *gin.Context) (logid string) { panic("unreachable code") } +func DumpHandler(c *Context) error { + errmsg := "api not implemented" + errcode := errCodeUnimplementApi + return c.RESULT_ERROR(errcode, errmsg) +} + +func ErrorHandler(c *gin.Context, errcode int, errmsg string) { + handler := handlerWrapper(func(c *Context) error { + return c.RESULT_ERROR(errcode, errmsg) + }) + handler(c) +} + func InitErrCodes(errcodes map[string]int) { if code, ok := errcodes["ErrCodeUnknownError"]; ok { errCodeUnknownError = code diff --git a/context.go b/context.go index 91c486e..c495427 100644 --- a/context.go +++ b/context.go @@ -25,6 +25,7 @@ type Context struct { resultentry string httpstatus int starttime time.Time + output interface{} } func New(c *gin.Context) *Context { @@ -89,6 +90,29 @@ func (c *Context) BindInput(i interface{}) (err error) { return nil } +func (c *Context) BindOutput(o interface{}) (err error) { + var v = reflect.ValueOf(o) + if v.Kind() != reflect.Pointer { + return fmt.Errorf("output is NOT a pointer") + } + + var e = v.Elem() + var t = e.Type() + if _, ok := t.FieldByName("ErrCode"); !ok { + return fmt.Errorf("out have no field ErrCode") + } + if _, ok := t.FieldByName("ErrMsg"); !ok { + return fmt.Errorf("out have no field ErrMsg") + } + if code := e.FieldByName("ErrCode"); !code.CanSet() { + return fmt.Errorf("output.ErrCode can not be set, " + + "output is NOT a pointer or output have no field 'ErrCode'") + } + + c.output = o + return nil +} + func (c *Context) SetCookie(cookie *http.Cookie) { if cookie.Path == "" { cookie.Path = "/" @@ -130,19 +154,46 @@ func (c *Context) RESULT(output interface{}) error { if _, ok := t.FieldByName("ErrCode"); !ok { errstr := "Result MUST have 'ErrCode' field" output = BaseWithErrCodeOutput{errCodeUnknownError, errstr} + c.ShapeOutput(output) c.IndentedJSON(c.httpstatus, output) return errors.New(errstr) } if _, ok := t.FieldByName("ErrMsg"); !ok { errstr := "Result MUST have 'ErrMsg' field" output = BaseWithErrCodeOutput{errCodeUnknownError, errstr} + c.ShapeOutput(output) c.IndentedJSON(c.httpstatus, output) return errors.New(errstr) } + c.ShapeOutput(output) c.IndentedJSON(c.httpstatus, output) return nil } +func (c *Context) ShapeOutput(o interface{}) { + if c.output == nil { + return + } + + var out, ok = o.(BaseWithErrCodeOutput) + if !ok { + return + } + + var e = reflect.ValueOf(c.output).Elem() + e.FieldByName("ErrMsg").SetString(out.ErrMsg) + e.FieldByName("ErrCode").SetInt(int64(out.ErrCode)) + return +} + +func (c *Context) Handler() Handler { + h := c.Context.Handler() + if handler, ok := handlerMapper[fmt.Sprintf("%v", h)]; ok { + return handler + } + return nil +} + func (c *Context) RESULT_ERROR(eno int, err string) error { if c.resultentry == "" { c.resultentry = "RESULT_ERROR" diff --git a/go.mod b/go.mod index 181b00a..09d8802 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module qoobing.com/gomod/api -go 1.16 +go 1.19 require ( github.com/gin-gonic/gin v1.8.1 diff --git a/routes.go b/routes.go index 8766ec1..9b3e17f 100644 --- a/routes.go +++ b/routes.go @@ -1,49 +1,43 @@ package api -import ( - "reflect" +// /////////////////////////////////////////////////////////////////// +// IRouter & IRoutes in gin, we redefine it simple +// /////////////////////////////////////////////////////////////////// +// +// // IRouter defines all router handle interface +// // includes single and group router. +// type IRouter interface { +// IRoutes +// Group(string, ...HandlerFunc) *RouterGroup +// } +// +// // IRoutes defines all router handle interface. +// type IRoutes interface { +// Use(...HandlerFunc) IRoutes +// +// Handle(string, string, ...HandlerFunc) IRoutes +// Any(string, ...HandlerFunc) IRoutes +// GET(string, ...HandlerFunc) IRoutes +// POST(string, ...HandlerFunc) IRoutes +// DELETE(string, ...HandlerFunc) IRoutes +// PATCH(string, ...HandlerFunc) IRoutes +// PUT(string, ...HandlerFunc) IRoutes +// OPTIONS(string, ...HandlerFunc) IRoutes +// HEAD(string, ...HandlerFunc) IRoutes +// +// StaticFile(string, string) IRoutes +// StaticFileFS(string, string, http.FileSystem) IRoutes +// Static(string, string) IRoutes +// StaticFS(string, http.FileSystem) IRoutes +// } +// +// /////////////////////////////////////////////////////////////////// - "github.com/gin-gonic/gin" - "qoobing.com/gomod/log" -) - -type routes struct { - gin.IRoutes +type Handler interface { + HandlerFunc() HandlerFunc } - type HandlerFunc func(*Context) error type IRoutes interface { - POST(uri string, handler HandlerFunc) -} - -func (r routes) POST(uri string, handler HandlerFunc) { - r.IRoutes.POST(uri, handlerWrapper(handler)) -} - -func DumpHandler(c *Context) error { - return c.RESULT_ERROR(errCodeUnimplementApi, "api not implemented") -} - -func ErrorHandler(c *gin.Context, errcode int, errmsg string) { - handler := handlerWrapper(func(c *Context) error { - return c.RESULT_ERROR(errcode, errmsg) - }) - handler(c) -} - -func handlerWrapper(handler HandlerFunc) gin.HandlerFunc { - return func(c *gin.Context) { - if icc, ok := c.Get("cc"); !ok { - log.Errorf("Unreachable Code: can not get cc(*api.Context) from *gin.Context") - } else if cc, ok := icc.(*Context); !ok { - log.Debugf("Unreachable Code: cc from *gin.Context is type of:[%s]", reflect.TypeOf(icc).String()) - log.Errorf("Unreachable Code: cc from *gin.Context is not type of *api.Context") - } else { - defer cc.TryRecover() - cc.AppId = c.GetString("appid") - cc.UserId = c.GetUint64("userid") - handler(cc) - } - } + POST(uri string, handler Handler) }