func main() {
engine := gin.New()
engine.GET("/time", WrapHandler(GetTime))
engine.Run(":8083")
}
type GetTimeReq struct {
Nation string `form:"nation" binding:"required" json:"nation"`
}
func GetTime(req *GetTimeReq) (*OKRsp, *ErrRsp) {
...
}
// WrapHandler wrap gin handler, the handler signature should be
// func(req *ReqStruct) (*OKRspStruct, *ErrorRspStruct) or
// func(c *gin.Context, req *ReqStruct) (*OKRspStruct, *ErrorRspStruct)
func WrapHandler(f interface{}) gin.HandlerFunc {
t := reflect.TypeOf(f)
if t.Kind() != reflect.Func {
panic("handdler should be function type")
}
fnumIn := t.NumIn()
if fnumIn == 0 || fnumIn > 2 {
panic("handler function require 1 or 2 input parameters")
}
if fnumIn == 2 {
tc := reflect.TypeOf(&gin.Context{})
if t.In(0) != tc {
panic("handler function first paramter should by type of *gin.Context if you have 2 input parameters")
}
}
if t.NumOut() != 2 {
panic("handler function return values should contain response data & error")
}
// errorInterface := reflect.TypeOf((*error)(nil)).Elem()
// if !t.Out(1).Implements(errorInterface) {
// panic("handler function second return value should by type of error")
// }
return func(c *gin.Context) {
var req interface{}
if fnumIn == 1 {
req = newReqInstance(t.In(0))
} else {
req = newReqInstance(t.In(1))
}
if !c.Bind(req) {
err := c.LastError()
log.Warning("bind parameter error:%v", err)
GinErrRsp(c, ErrCodeParamInvalid, err.Error())
return
}
var inValues []reflect.Value
if fnumIn == 1 {
inValues = []reflect.Value{reflect.ValueOf(req)}
} else {
inValues = []reflect.Value{reflect.ValueOf(c), reflect.ValueOf(req)}
}
ret := reflect.ValueOf(f).Call(inValues)
if ret[1].IsNil() {
GinRsp(c, http.StatusOK, ret[0].Interface())
} else {
GinRsp(c, http.StatusOK, ret[1].Interface())
}
}
}
func newReqInstance(t reflect.Type) interface{} {
switch t.Kind() {
case reflect.Ptr, reflect.Interface:
return newReqInstance(t.Elem())
default:
return reflect.New(t).Interface()
}
}