add bindoutput & rewrite shapeoutput

This commit is contained in:
bryanqiu 2022-12-06 19:15:35 +08:00
parent 8489fc1556
commit f978fa3f87
4 changed files with 125 additions and 44 deletions

40
api.go
View File

@ -1,6 +1,9 @@
package api package api
import ( import (
"fmt"
"reflect"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"qoobing.com/gomod/log" "qoobing.com/gomod/log"
@ -10,6 +13,7 @@ import (
var ( var (
validate = validator.New() validate = validator.New()
requestLogidGetter = defaultLogidGetter requestLogidGetter = defaultLogidGetter
handlerMapper = map[string]Handler{}
errCodeUnknownError = -10000 errCodeUnknownError = -10000
errCodeUnimplementApi = -20401 errCodeUnimplementApi = -20401
) )
@ -40,8 +44,27 @@ func NewEngine() *Engine {
return &Engine{e} return &Engine{e}
} }
func (e *Engine) POST(uri string, handler HandlerFunc) { func (e *Engine) POST(uri string, handler Handler) {
e.Engine.POST(uri, handlerWrapper(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 // middlewareMyApiEngine myapi engine middleware
@ -72,6 +95,19 @@ func defaultLogidGetter(c *gin.Context) (logid string) {
panic("unreachable code") 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) { func InitErrCodes(errcodes map[string]int) {
if code, ok := errcodes["ErrCodeUnknownError"]; ok { if code, ok := errcodes["ErrCodeUnknownError"]; ok {
errCodeUnknownError = code errCodeUnknownError = code

View File

@ -25,6 +25,7 @@ type Context struct {
resultentry string resultentry string
httpstatus int httpstatus int
starttime time.Time starttime time.Time
output interface{}
} }
func New(c *gin.Context) *Context { func New(c *gin.Context) *Context {
@ -89,6 +90,29 @@ func (c *Context) BindInput(i interface{}) (err error) {
return nil 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) { func (c *Context) SetCookie(cookie *http.Cookie) {
if cookie.Path == "" { if cookie.Path == "" {
cookie.Path = "/" cookie.Path = "/"
@ -130,19 +154,46 @@ func (c *Context) RESULT(output interface{}) error {
if _, ok := t.FieldByName("ErrCode"); !ok { if _, ok := t.FieldByName("ErrCode"); !ok {
errstr := "Result MUST have 'ErrCode' field" errstr := "Result MUST have 'ErrCode' field"
output = BaseWithErrCodeOutput{errCodeUnknownError, errstr} output = BaseWithErrCodeOutput{errCodeUnknownError, errstr}
c.ShapeOutput(output)
c.IndentedJSON(c.httpstatus, output) c.IndentedJSON(c.httpstatus, output)
return errors.New(errstr) return errors.New(errstr)
} }
if _, ok := t.FieldByName("ErrMsg"); !ok { if _, ok := t.FieldByName("ErrMsg"); !ok {
errstr := "Result MUST have 'ErrMsg' field" errstr := "Result MUST have 'ErrMsg' field"
output = BaseWithErrCodeOutput{errCodeUnknownError, errstr} output = BaseWithErrCodeOutput{errCodeUnknownError, errstr}
c.ShapeOutput(output)
c.IndentedJSON(c.httpstatus, output) c.IndentedJSON(c.httpstatus, output)
return errors.New(errstr) return errors.New(errstr)
} }
c.ShapeOutput(output)
c.IndentedJSON(c.httpstatus, output) c.IndentedJSON(c.httpstatus, output)
return nil 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 { func (c *Context) RESULT_ERROR(eno int, err string) error {
if c.resultentry == "" { if c.resultentry == "" {
c.resultentry = "RESULT_ERROR" c.resultentry = "RESULT_ERROR"

2
go.mod
View File

@ -1,6 +1,6 @@
module qoobing.com/gomod/api module qoobing.com/gomod/api
go 1.16 go 1.19
require ( require (
github.com/gin-gonic/gin v1.8.1 github.com/gin-gonic/gin v1.8.1

View File

@ -1,49 +1,43 @@
package api 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" type Handler interface {
"qoobing.com/gomod/log" HandlerFunc() HandlerFunc
)
type routes struct {
gin.IRoutes
} }
type HandlerFunc func(*Context) error type HandlerFunc func(*Context) error
type IRoutes interface { type IRoutes interface {
POST(uri string, handler HandlerFunc) POST(uri string, handler Handler)
}
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)
}
}
} }