github.com/wfusion/gofusion@v1.1.14/http/router.go (about)

     1  package http
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"mime"
    10  	"net/http"
    11  	"reflect"
    12  	"time"
    13  
    14  	"github.com/gin-gonic/gin"
    15  	"github.com/gin-gonic/gin/binding"
    16  	"github.com/mitchellh/mapstructure"
    17  	"github.com/pkg/errors"
    18  	"github.com/spf13/cast"
    19  
    20  	"github.com/wfusion/gofusion/common/constant"
    21  	"github.com/wfusion/gofusion/common/utils"
    22  	"github.com/wfusion/gofusion/common/utils/clone"
    23  	"github.com/wfusion/gofusion/config"
    24  	"github.com/wfusion/gofusion/http/gracefully"
    25  	"github.com/wfusion/gofusion/http/parser"
    26  	"github.com/wfusion/gofusion/routine"
    27  )
    28  
    29  type parseFrom int
    30  
    31  const (
    32  	parseFromBody parseFrom = 1 + iota
    33  	parseFromQuery
    34  )
    35  
    36  type dispatch int
    37  
    38  const (
    39  	dispatchIRouter dispatch = iota
    40  	dispatchGroup
    41  	dispatchRoutes
    42  )
    43  
    44  type routerHandler any
    45  type routerRequestParser func(*gin.Context, reflect.Type) (reflect.Value, error)
    46  
    47  var (
    48  	methodWithBody = map[string]bool{
    49  		http.MethodPut:   true,
    50  		http.MethodPost:  true,
    51  		http.MethodPatch: true,
    52  	}
    53  )
    54  
    55  type router struct {
    56  	gin.IRouter
    57  
    58  	open         chan struct{}
    59  	close        chan struct{}
    60  	ctx          context.Context
    61  	appName      string
    62  	successCode  int
    63  	errorCode    Errcode
    64  	shutdownFunc func()
    65  	metricsConf  metricsConf
    66  
    67  	routes gin.IRoutes      `optional:"true"`
    68  	group  *gin.RouterGroup `optional:"true"`
    69  	ptr    dispatch         `optional:"true"`
    70  }
    71  
    72  func newRouter(ctx context.Context, r gin.IRouter, appName string, successCode, errorCode int) IRouter {
    73  	return &router{
    74  		IRouter:     r,
    75  		ctx:         ctx,
    76  		open:        make(chan struct{}),
    77  		close:       make(chan struct{}),
    78  		appName:     appName,
    79  		successCode: successCode,
    80  		errorCode:   Errcode(errorCode),
    81  	}
    82  }
    83  
    84  func (r *router) Use(middlewares ...gin.HandlerFunc) IRouter {
    85  	return &router{
    86  		IRouter:      r.IRouter,
    87  		open:         r.open,
    88  		close:        r.close,
    89  		ctx:          r.ctx,
    90  		appName:      r.appName,
    91  		successCode:  r.successCode,
    92  		errorCode:    r.errorCode,
    93  		shutdownFunc: r.shutdownFunc,
    94  		routes:       r.use().Use(middlewares...),
    95  		group:        r.group,
    96  		ptr:          dispatchRoutes,
    97  	}
    98  }
    99  
   100  func (r *router) Handle(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   101  	opt := utils.ApplyOptions[routerOption](opts...)
   102  	r.use().HEAD(uri, r.convertMulti("Handle", uri, fn, opt)...)
   103  	return r
   104  }
   105  func (r *router) Any(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   106  	opt := utils.ApplyOptions[routerOption](opts...)
   107  	r.use().Any(uri, r.convertMulti("Any", uri, fn, opt)...)
   108  	return r
   109  }
   110  func (r *router) GET(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   111  	opt := utils.ApplyOptions[routerOption](opts...)
   112  	r.use().GET(uri, r.convertMulti(http.MethodGet, uri, fn, opt)...)
   113  	return r
   114  }
   115  func (r *router) POST(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   116  	opt := utils.ApplyOptions[routerOption](opts...)
   117  	r.use().POST(uri, r.convertMulti(http.MethodPost, uri, fn, opt)...)
   118  	return r
   119  }
   120  func (r *router) DELETE(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   121  	opt := utils.ApplyOptions[routerOption](opts...)
   122  	r.use().DELETE(uri, r.convertMulti(http.MethodDelete, uri, fn, opt)...)
   123  	return r
   124  }
   125  func (r *router) PATCH(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   126  	opt := utils.ApplyOptions[routerOption](opts...)
   127  	r.use().PATCH(uri, r.convertMulti(http.MethodPatch, uri, fn, opt)...)
   128  	return r
   129  }
   130  func (r *router) PUT(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   131  	opt := utils.ApplyOptions[routerOption](opts...)
   132  	r.use().PUT(uri, r.convertMulti(http.MethodPut, uri, fn, opt)...)
   133  	return r
   134  }
   135  func (r *router) OPTIONS(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   136  	opt := utils.ApplyOptions[routerOption](opts...)
   137  	r.use().OPTIONS(uri, r.convertMulti(http.MethodOptions, uri, fn, opt)...)
   138  	return r
   139  }
   140  func (r *router) HEAD(uri string, fn routerHandler, opts ...utils.OptionExtender) IRouter {
   141  	opt := utils.ApplyOptions[routerOption](opts...)
   142  	r.use().HEAD(uri, r.convertMulti(http.MethodHead, uri, fn, opt)...)
   143  	return r
   144  }
   145  func (r *router) Group(relativePath string, handlers ...gin.HandlerFunc) IRouter {
   146  	return &router{
   147  		IRouter:      r.IRouter,
   148  		open:         r.open,
   149  		close:        r.close,
   150  		ctx:          r.ctx,
   151  		appName:      r.appName,
   152  		successCode:  r.successCode,
   153  		errorCode:    r.errorCode,
   154  		shutdownFunc: r.shutdownFunc,
   155  		routes:       r.routes,
   156  		group:        r.useIRouter().Group(relativePath, handlers...),
   157  		ptr:          dispatchGroup,
   158  	}
   159  }
   160  
   161  func (r *router) StaticFile(uri, file string) IRouter { r.use().StaticFile(uri, file); return r }
   162  func (r *router) StaticFileFS(uri, file string, fs http.FileSystem) IRouter {
   163  	r.use().StaticFileFS(uri, file, fs)
   164  	return r
   165  }
   166  func (r *router) Static(uri, file string) IRouter { r.use().Static(uri, file); return r }
   167  func (r *router) StaticFS(uri string, fs http.FileSystem) IRouter {
   168  	r.use().StaticFS(uri, fs)
   169  	return r
   170  }
   171  
   172  func (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   173  	r.IRouter.(*gin.Engine).ServeHTTP(w, req)
   174  }
   175  func (r *router) ListenAndServe() (err error) {
   176  	if _, ok := utils.IsChannelClosed(r.open); ok {
   177  		<-r.Closing()
   178  		return
   179  	}
   180  
   181  	conf := r.Config()
   182  	gracefully.DefaultReadTimeOut = conf.ReadTimeout
   183  	gracefully.DefaultWriteTimeOut = conf.WriteTimeout
   184  	gracefully.DefaultMaxHeaderBytes = 1 << 20 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
   185  
   186  	port := fmt.Sprintf(":%v", conf.Port)
   187  	srv := gracefully.NewServer(r.appName, r.IRouter.(*gin.Engine), port, conf.NextProtos)
   188  	r.shutdownFunc = srv.Shutdown
   189  
   190  	r.close = make(chan struct{})
   191  	close(r.open)
   192  	defer func() {
   193  		close(r.close)
   194  		r.open = make(chan struct{})
   195  	}()
   196  
   197  	if conf.TLS {
   198  		return srv.ListenAndServeTLS(conf.Cert, conf.Key)
   199  	} else {
   200  		return srv.ListenAndServe()
   201  	}
   202  }
   203  func (r *router) Start() {
   204  	if _, ok := utils.IsChannelClosed(r.open); ok {
   205  		return
   206  	}
   207  	conf := r.Config()
   208  	gracefully.DefaultReadTimeOut = conf.ReadTimeout
   209  	gracefully.DefaultWriteTimeOut = conf.WriteTimeout
   210  	gracefully.DefaultMaxHeaderBytes = 1 << 20 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
   211  
   212  	port := fmt.Sprintf(":%v", conf.Port)
   213  	srv := gracefully.NewServer(r.appName, r.IRouter.(*gin.Engine), port, conf.NextProtos)
   214  	r.shutdownFunc = srv.Shutdown
   215  	if conf.TLS {
   216  		routine.Go(srv.ListenAndServeTLS, routine.Args(conf.Cert, conf.Key), routine.AppName(r.appName))
   217  	} else {
   218  		routine.Go(srv.ListenAndServe, routine.AppName(r.appName))
   219  	}
   220  
   221  	r.close = make(chan struct{})
   222  	close(r.open)
   223  }
   224  func (r *router) Config() OutputConf {
   225  	cfg := new(Conf)
   226  	_ = config.Use(r.appName).LoadComponentConfig(config.ComponentHttp, cfg)
   227  
   228  	return OutputConf{
   229  		Port:         cfg.Port,
   230  		TLS:          cfg.TLS,
   231  		Cert:         cfg.Cert,
   232  		Key:          cfg.Key,
   233  		NextProtos:   cfg.NextProtos,
   234  		SuccessCode:  cfg.SuccessCode,
   235  		ReadTimeout:  utils.Must(time.ParseDuration(cfg.ReadTimeout)),
   236  		WriteTimeout: utils.Must(time.ParseDuration(cfg.WriteTimeout)),
   237  		AsynqConf:    clone.Slowly(cfg.Asynq),
   238  	}
   239  }
   240  func (r *router) Running() <-chan struct{} { return r.open }
   241  func (r *router) Closing() <-chan struct{} { return r.close }
   242  
   243  func (r *router) shutdown() {
   244  	if r.close != nil {
   245  		if _, ok := utils.IsChannelClosed(r.close); ok {
   246  			return
   247  		}
   248  	}
   249  	if r.shutdownFunc != nil {
   250  		r.shutdownFunc()
   251  	}
   252  	if r.close != nil {
   253  		close(r.close)
   254  	}
   255  
   256  	r.open = make(chan struct{})
   257  }
   258  
   259  func (r *router) use() gin.IRoutes {
   260  	switch r.ptr {
   261  	case dispatchIRouter:
   262  		return r.IRouter
   263  	case dispatchGroup:
   264  		return r.group
   265  	case dispatchRoutes:
   266  		return r.routes
   267  	default:
   268  		return r.IRouter
   269  	}
   270  }
   271  
   272  func (r *router) useIRouter() gin.IRouter {
   273  	switch r.ptr {
   274  	case dispatchIRouter:
   275  		return r.IRouter
   276  	case dispatchGroup:
   277  		return r.group
   278  	case dispatchRoutes:
   279  		panic(errors.New("group method unsupported for gin.Routes interface"))
   280  	default:
   281  		return r.IRouter
   282  	}
   283  }
   284  
   285  // convert
   286  // Warning: MultipartFormDataBody only support Struct or *Struct
   287  // support router handler signature as follows:
   288  // - be compatible with native func(c *gin.Context) without any in&out parameters parsed
   289  // - func(c *gin.Context, req Struct FromQuery) error
   290  // - func(c *gin.Context, req Struct FromJsonBody) error
   291  // - func(c *gin.Context, req Struct FromFormUrlDecodedBody) error
   292  // - func(c *gin.Context, req Struct FromMultipartFormDataBody) error
   293  // - func(c *gin.Context, req *Struct FromQuery) error
   294  // - func(c *gin.Context, req *Struct FromParam) error
   295  // - func(c *gin.Context, req *Struct FromJsonBody) error
   296  // - func(c *gin.Context, req *Struct FromFormUrlDecodedBody) error
   297  // - func(c *gin.Context, req *Struct FromMultipartFormDataBody) error
   298  // - func(c *gin.Context, req map[string]any FromQuery) error
   299  // - func(c *gin.Context, req map[string]any FromJsonBody) error
   300  // - func(c *gin.Context, req map[string]any FromFormUrlDecodedBody) error
   301  // - func(c *gin.Context, req []map[string]any FromQuery) error
   302  // - func(c *gin.Context, req []map[string]any FromJsonBody) error
   303  // - func(c *gin.Context, req []map[string]any FromFormUrlDecodedBody) error
   304  // - func(c *gin.Context, req *FromQuery) (rsp *Struct{Data any; Page, Count int; Msg string}, err error)
   305  // - func(c *gin.Context, req *FromQuery) (rsp *Struct{Embed}, err error)
   306  // - func(c *gin.Context, req *FromQuery) (data any, page, count int, msg string, err error)
   307  // - class.public.func(c *gin.Context, req Struct FromQuery) error
   308  // - class.private.func(c *gin.Context, req Struct FromQuery) error
   309  func (r *router) convert(method, uri string, handler routerHandler, opt *routerOption) gin.HandlerFunc {
   310  	// check IRouter handler type
   311  	typ := reflect.TypeOf(handler)
   312  	if err := r.checkHandlerType(method, uri, typ); err != nil {
   313  		panic(err)
   314  	}
   315  
   316  	// return raw gin handler
   317  	if typ.NumIn() == 1 && typ.NumOut() == 0 {
   318  		return func(c *gin.Context) {
   319  			reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(c)})
   320  			c.Next()
   321  		}
   322  	}
   323  
   324  	// parse request&response
   325  	if typ.NumIn() == 1 {
   326  		return r.wrapHandlerFunc(handler, nil)
   327  	}
   328  
   329  	parseMap := map[parseFrom]routerRequestParser{
   330  		parseFromBody:  r.parseReqFromBody,
   331  		parseFromQuery: r.parseReqFromQuery,
   332  	}
   333  
   334  	var reqParse routerRequestParser
   335  	if p, ok := parseMap[opt.parseFrom]; ok {
   336  		reqParse = p
   337  	} else if methodWithBody[method] {
   338  		reqParse = r.parseReqFromBody
   339  	} else {
   340  		reqParse = r.parseReqFromQuery
   341  	}
   342  
   343  	return r.wrapHandlerFunc(handler, reqParse)
   344  }
   345  
   346  func (r *router) convertMulti(method, uri string, hdr routerHandler, opt *routerOption) (result gin.HandlersChain) {
   347  	result = make(gin.HandlersChain, 0, len(opt.beforeHandlers)+len(opt.aftersHandlers)+1)
   348  	for _, hdr := range opt.beforeHandlers {
   349  		result = append(result, r.convert(method, uri, hdr, opt))
   350  	}
   351  	result = append(result, r.convert(method, uri, hdr, opt))
   352  	for _, hdr := range opt.aftersHandlers {
   353  		result = append(result, r.convert(method, uri, hdr, opt))
   354  	}
   355  	return
   356  }
   357  
   358  func (r *router) checkHandlerType(method, uri string, typ reflect.Type) (err error) {
   359  	if typ.Kind() != reflect.Func {
   360  		return errors.Errorf("router handler should be a function [method[%s] uri[%s]]", method, uri)
   361  	}
   362  
   363  	// check in
   364  	if typ.NumIn() < 1 {
   365  		return errors.Errorf("router handler should have at least 1 parameter in "+
   366  			"[method[%s] uri[%s]]", method, uri)
   367  	}
   368  	if typ.NumIn() > 2 {
   369  		return errors.Errorf("router handler should not have more than 2 parameters in "+
   370  			"[method[%s] uri[%s]]", method, uri)
   371  	}
   372  	if typ.In(0) != constant.GinContextType {
   373  		return errors.Errorf("router handler first parameter in should be *gin.Context "+
   374  			"[method[%s] uri[%s]]", method, uri)
   375  	}
   376  	if typ.NumIn() == 2 {
   377  		if !r.checkParamType(typ.In(1), supportParamType) {
   378  			return errors.Errorf("router handler second parameter in type not supportted "+
   379  				"[method[%s] uri[%s]]", method, uri)
   380  		}
   381  	}
   382  
   383  	// check out
   384  
   385  	// check error
   386  	if typ.NumOut() > 0 && !typ.Out(typ.NumOut()-1).AssignableTo(constant.ErrorType) {
   387  		return errors.Errorf("router handler last paramater out should be error type "+
   388  			"[method[%s] uri[%s]]", method, uri)
   389  	}
   390  
   391  	// check (data any, page, count int, msg string, err error)
   392  	if numOut := typ.NumOut(); numOut > 1 {
   393  		supportTypes := supportReturnTypeList[numOut-1]
   394  		for i := 0; i < numOut-1; i++ {
   395  			if !r.checkParamType(typ.Out(i), supportTypes[i]) {
   396  				return errors.Errorf("router handler paramater out format is illegal "+
   397  					"[method[%s] uri[%s] index[%v] unsupported[%s] suppoted[%+v]]",
   398  					method, uri, i+1, typ.Out(i).Kind(), supportTypes[i])
   399  			}
   400  		}
   401  	}
   402  
   403  	return
   404  }
   405  
   406  var (
   407  	supportReturnTypeList = map[int][]map[reflect.Kind]struct{}{
   408  		1: {supportDataType},                                                 // data
   409  		2: {supportDataType, supportMsgType},                                 // data, msg
   410  		3: {supportDataType, supportIntType, supportMsgType},                 // data, count, msg
   411  		4: {supportDataType, supportIntType, supportIntType, supportMsgType}, // data, page, count, msg
   412  	}
   413  	supportIntType = map[reflect.Kind]struct{}{
   414  		reflect.Int:   {},
   415  		reflect.Int8:  {},
   416  		reflect.Int16: {},
   417  		reflect.Int32: {},
   418  		reflect.Int64: {},
   419  	}
   420  	supportDataType = map[reflect.Kind]struct{}{
   421  		reflect.Map:       {},
   422  		reflect.Array:     {},
   423  		reflect.Slice:     {},
   424  		reflect.Struct:    {},
   425  		reflect.Interface: {},
   426  	}
   427  	supportMsgType = map[reflect.Kind]struct{}{
   428  		reflect.String: {},
   429  	}
   430  	supportParamType = map[reflect.Kind]struct{}{
   431  		reflect.Map:    {},
   432  		reflect.Array:  {},
   433  		reflect.Slice:  {},
   434  		reflect.Struct: {},
   435  	}
   436  )
   437  
   438  func (r *router) checkParamType(typ reflect.Type, supportType map[reflect.Kind]struct{}) bool {
   439  	if typ.Kind() == reflect.Ptr {
   440  		typ = typ.Elem()
   441  	}
   442  	_, ok := supportType[typ.Kind()]
   443  	return ok
   444  }
   445  
   446  func (r *router) parseReqFromBody(c *gin.Context, typ reflect.Type) (dst reflect.Value, err error) {
   447  	ptrDepth := 0
   448  	for typ.Kind() == reflect.Ptr {
   449  		typ = typ.Elem()
   450  		ptrDepth++
   451  	}
   452  	defer func() {
   453  		for ptrDepth > 0 {
   454  			dst = dst.Addr()
   455  			ptrDepth--
   456  		}
   457  	}()
   458  
   459  	bodyBytes, _ := io.ReadAll(c.Request.Body)
   460  	c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
   461  
   462  	dst = reflect.Indirect(reflect.New(typ))
   463  	if err = c.ShouldBind(dst.Addr().Interface()); err != nil &&
   464  		!(errors.Is(err, binding.ErrConvertToMapString) || (errors.Is(err, binding.ErrConvertMapStringSlice))) {
   465  		err = parseGinBindingValidatorError(err)
   466  		return
   467  	}
   468  	defer func() { c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) }()
   469  
   470  	if dst.IsZero() {
   471  		var (
   472  			p           parser.Parser
   473  			param       map[string]string
   474  			contentType string
   475  		)
   476  		if contentType, param, err = mime.ParseMediaType(c.GetHeader("Content-Type")); err != nil {
   477  			return
   478  		}
   479  		if p, err = parser.GetByContentType(contentType); err != nil {
   480  			return
   481  		}
   482  		if err = p.PreParse(param); err != nil {
   483  			return
   484  		}
   485  		c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
   486  		if err = p.Parse(c.Request.Body, dst); err != nil {
   487  			return
   488  		}
   489  	}
   490  
   491  	err = utils.ParseTag(
   492  		dst.Addr().Interface(),
   493  		utils.ParseTagName("default"),
   494  		utils.ParseTagUnmarshalType(utils.UnmarshalTypeYaml),
   495  	)
   496  
   497  	return
   498  }
   499  
   500  func (r *router) parseReqFromQuery(c *gin.Context, typ reflect.Type) (dst reflect.Value, err error) {
   501  	ptrDepth := 0
   502  	for typ.Kind() == reflect.Ptr {
   503  		typ = typ.Elem()
   504  		ptrDepth++
   505  	}
   506  	defer func() {
   507  		for ptrDepth > 0 {
   508  			dst = dst.Addr()
   509  			ptrDepth--
   510  		}
   511  	}()
   512  
   513  	dst = reflect.Indirect(reflect.New(typ))
   514  	if err = c.ShouldBindUri(dst.Addr().Interface()); err != nil &&
   515  		!(errors.Is(err, binding.ErrConvertToMapString) || (errors.Is(err, binding.ErrConvertMapStringSlice))) {
   516  		err = parseGinBindingValidatorError(err)
   517  		return
   518  	}
   519  	if err = c.ShouldBindQuery(dst.Addr().Interface()); err != nil &&
   520  		!(errors.Is(err, binding.ErrConvertToMapString) || (errors.Is(err, binding.ErrConvertMapStringSlice))) {
   521  		err = parseGinBindingValidatorError(err)
   522  		return
   523  	}
   524  	// support query with json tag
   525  	if dst.IsZero() {
   526  		m := make(map[string][]string)
   527  		for _, v := range c.Params {
   528  			m[v.Key] = []string{v.Value}
   529  		}
   530  		err = utils.CheckIfAny(
   531  			func() error { return parser.MapFormByTag(dst.Addr().Interface(), m, "json") },
   532  			func() error { return parser.MapFormByTag(dst.Addr().Interface(), c.Request.URL.Query(), "json") },
   533  		)
   534  	}
   535  
   536  	// parse default tag
   537  	err = utils.ParseTag(
   538  		dst.Addr().Interface(),
   539  		utils.ParseTagName("default"),
   540  		utils.ParseTagUnmarshalType(utils.UnmarshalTypeYaml),
   541  	)
   542  
   543  	return
   544  }
   545  
   546  func (r *router) wrapHandlerFunc(handler routerHandler, reqParse routerRequestParser) gin.HandlerFunc {
   547  	typ := reflect.TypeOf(handler)
   548  	numOut := typ.NumOut()
   549  	return func(c *gin.Context) {
   550  		var (
   551  			err     error
   552  			reqVal  reflect.Value
   553  			rspVals []reflect.Value
   554  		)
   555  
   556  		// deal with request & call handler
   557  		if reqParse == nil {
   558  			rspVals = reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(c)})
   559  		} else if reqVal, err = reqParse(c, typ.In(1)); err == nil {
   560  			rspVals = reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(c), reqVal})
   561  		} else {
   562  			switch e := err.(type) {
   563  			case *json.UnmarshalTypeError:
   564  				msg := fmt.Sprintf(": %s field type should be %s", e.Value, e.Type.String())
   565  				r.rspError(c, nil, Err(c, r.errorCode, Param(map[string]any{"err": msg})))
   566  			default:
   567  				r.rspError(c, nil, Err(c, r.errorCode,
   568  					Param(map[string]any{"err": fmt.Sprintf(": %s", err.Error())})))
   569  			}
   570  			c.Next()
   571  			return
   572  		}
   573  
   574  		// deal with response
   575  		errVal := rspVals[numOut-1]
   576  		if !errVal.IsNil() {
   577  			err = errVal.Interface().(error)
   578  		}
   579  		rspVals = rspVals[:numOut-1]
   580  
   581  		var rspType reflect.Type
   582  		isEmbed, isResponse := false, false
   583  		if len(rspVals) > 0 {
   584  			rspType = utils.IndirectType(rspVals[0].Type())
   585  			isEmbed = utils.EmbedsType(rspType, embedType)
   586  			isResponse = utils.EmbedsType(rspType, responseType)
   587  		}
   588  
   589  		switch {
   590  		case isEmbed, isResponse:
   591  			r.rspEmbed(c, rspVals[0], rspType, err) // directly json marshal embed response
   592  		case err != nil:
   593  			r.rspError(c, rspVals, err) // business error
   594  		default:
   595  			r.rspSuccess(c, rspVals) // success with response
   596  		}
   597  
   598  		c.Next()
   599  	}
   600  }
   601  
   602  func (r *router) rspError(c *gin.Context, rspVals []reflect.Value, err error) {
   603  	code, data, page, count, msg := parseRspError(rspVals, err)
   604  	rspError(c, r.appName, code, data, page, count, msg)
   605  
   606  	go metricsCode(r.ctx, r.appName, c.Request.URL.Path, c.Request.Method, r.parseHeaderMetrics(c),
   607  		cast.ToInt(code), c.Writer.Status(), c.Writer.Size(), c.Request.ContentLength)
   608  }
   609  
   610  func (r *router) rspSuccess(c *gin.Context, rspVals []reflect.Value) {
   611  	data, page, count, msg := parseRspSuccess(rspVals)
   612  	rspSuccess(c, r.successCode, data, page, count, msg)
   613  
   614  	go metricsCode(r.ctx, r.appName, c.Request.URL.Path, c.Request.Method, r.parseHeaderMetrics(c),
   615  		r.successCode, c.Writer.Status(), c.Writer.Size(), c.Request.ContentLength)
   616  }
   617  
   618  func (r *router) rspEmbed(c *gin.Context, rspVal reflect.Value, rspType reflect.Type, err error) {
   619  	var data any
   620  	if rspVal.IsValid() {
   621  		data = rspVal.Interface()
   622  	} else {
   623  		data = reflect.New(rspType).Interface()
   624  	}
   625  
   626  	embedResponse(c, data, err)
   627  
   628  	switch rsp := data.(type) {
   629  	case Response:
   630  		go metricsCode(r.ctx, r.appName, c.Request.URL.Path, c.Request.Method, r.parseHeaderMetrics(c),
   631  			rsp.Code, c.Writer.Status(), c.Writer.Size(), c.Request.ContentLength)
   632  	case *Response:
   633  		go metricsCode(r.ctx, r.appName, c.Request.URL.Path, c.Request.Method, r.parseHeaderMetrics(c),
   634  			rsp.Code, c.Writer.Status(), c.Writer.Size(), c.Request.ContentLength)
   635  	default:
   636  		rspData := make(map[string]any)
   637  		dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   638  			Result:  &rspData,
   639  			TagName: "json",
   640  		})
   641  		if err == nil && dec != nil {
   642  			_ = dec.Decode(data)
   643  		}
   644  		var code any
   645  		utils.IfAny(
   646  			func() (ok bool) { code, ok = rspData["code"]; return ok },
   647  			func() (ok bool) { code, ok = rspData["Code"]; return ok },
   648  		)
   649  		if code == nil {
   650  			code = -2
   651  		}
   652  		go metricsCode(r.ctx, r.appName, c.Request.URL.Path, c.Request.Method, r.parseHeaderMetrics(c),
   653  			cast.ToInt(code), c.Writer.Status(), c.Writer.Size(), c.Request.ContentLength)
   654  	}
   655  }
   656  
   657  func (r *router) parseHeaderMetrics(c *gin.Context) (headerLabels map[string]string) {
   658  	headerLabels = make(map[string]string, len(r.metricsConf.HeaderLabels))
   659  	for _, metricsHeader := range r.metricsConf.HeaderLabels {
   660  		headerLabels[metricsHeader] = c.Request.Header.Get(metricsHeader)
   661  	}
   662  	return
   663  }
   664  
   665  // 0: return error
   666  // 1: return any, error
   667  //    parse as map/struct -> data, msg, count, page
   668  //    parse as others     -> data -> {"code": 0, "data": []} or {"code": 0, "data": {}}
   669  // 2: return data, msg, error
   670  // 3: return data, count, msg, error
   671  // >3: return data, page, count, msg, unknowns..., error
   672  func parseRspSuccess(rspVals []reflect.Value) (data any, page, count int, msg string) {
   673  	switch {
   674  	case len(rspVals) == 0:
   675  	case len(rspVals) == 2:
   676  		data = transformData(rspVals[0])
   677  		msg = reflect.Indirect(rspVals[1]).String()
   678  	case len(rspVals) == 3:
   679  		data = transformData(rspVals[0])
   680  		count = cast.ToInt(reflect.Indirect(rspVals[1]).Int())
   681  		msg = reflect.Indirect(rspVals[2]).String()
   682  	case len(rspVals) > 3:
   683  		data = transformData(rspVals[0])
   684  		page = cast.ToInt(reflect.Indirect(rspVals[1]).Int())
   685  		count = cast.ToInt(reflect.Indirect(rspVals[2]).Int())
   686  		msg = reflect.Indirect(rspVals[3]).String()
   687  	default:
   688  		data, page, count, msg = lookupFieldByStruct(rspVals[0])
   689  	}
   690  
   691  	return
   692  }
   693  
   694  func parseRspError(rspVals []reflect.Value, err error) (code int, data any, page, count int, msg string) {
   695  	switch e := err.(type) {
   696  	case Errcode:
   697  		code, msg = int(e), e.Error()
   698  	case *bizErr:
   699  		code, msg = int(e.code), e.Error()
   700  	default:
   701  		code, msg = int(errParam), e.Error()
   702  	}
   703  
   704  	data, page, count, retMsg := parseRspSuccess(rspVals)
   705  	if retMsg != "" {
   706  		msg = retMsg
   707  	}
   708  
   709  	return
   710  }
   711  
   712  func lookupFieldByStruct(rspStruct reflect.Value) (data any, page, count int, msg string) {
   713  	var (
   714  		routerResponsePageName = map[string]bool{
   715  			"page": true,
   716  			"Page": true,
   717  		}
   718  		routerResponseCountName = map[string]bool{
   719  			"count": true,
   720  			"Count": true,
   721  		}
   722  		routerResponseDataName = map[string]bool{
   723  			"data": true,
   724  			"Data": true,
   725  		}
   726  		routerResponseMsgName = map[string]bool{
   727  			"msg":     true,
   728  			"Msg":     true,
   729  			"message": true,
   730  			"Message": true,
   731  		}
   732  	)
   733  
   734  	type lookupFunc func(v reflect.Value, keywords map[string]bool) (reflect.Value, bool)
   735  	lookupFuncMap := map[reflect.Kind]lookupFunc{
   736  		reflect.Map:    lookupFieldByMap,
   737  		reflect.Struct: lookupFieldByValue,
   738  	}
   739  
   740  	rsp := utils.IndirectValue(rspStruct)
   741  	lookup, ok := lookupFuncMap[rsp.Kind()]
   742  	if !ok {
   743  		data = rspStruct.Interface()
   744  		return
   745  	}
   746  
   747  	// cannot parse response.Data, resolve all rspStruct as data
   748  	if dataValue, ok := lookup(rsp, routerResponseDataName); ok {
   749  		if dataValue.IsValid() {
   750  			data = transformData(reflect.ValueOf(valueInterface(dataValue, false)))
   751  		}
   752  	} else {
   753  		data = rspStruct.Interface()
   754  		return
   755  	}
   756  	if pageValue, ok := lookup(rsp, routerResponsePageName); ok && pageValue.IsValid() {
   757  		page = cast.ToInt(pageValue.Int())
   758  	}
   759  	if countValue, ok := lookup(rsp, routerResponseCountName); ok && countValue.IsValid() {
   760  		count = cast.ToInt(countValue.Int())
   761  	}
   762  	if msgValue, ok := lookup(rsp, routerResponseMsgName); ok && msgValue.IsValid() {
   763  		msg = msgValue.String()
   764  	}
   765  
   766  	return
   767  }
   768  
   769  func lookupFieldByValue(v reflect.Value, keywords map[string]bool) (reflect.Value, bool) {
   770  	f := reflect.Indirect(v.FieldByNameFunc(func(s string) bool { return keywords[s] }))
   771  	if !f.IsValid() || f.IsZero() {
   772  		return reflect.Value{}, false
   773  	}
   774  	return reflect.Indirect(f), true
   775  }
   776  
   777  func lookupFieldByMap(v reflect.Value, keywords map[string]bool) (reflect.Value, bool) {
   778  	vMap, ok := v.Interface().(map[string]any)
   779  	if !ok {
   780  		return reflect.Value{}, false
   781  	}
   782  
   783  	for key := range keywords {
   784  		if vv, ok := vMap[key]; ok {
   785  			return reflect.ValueOf(vv), true
   786  		}
   787  	}
   788  	return reflect.Value{}, false
   789  }
   790  
   791  func transformData(data reflect.Value) (transformed any) {
   792  	if !data.IsValid() {
   793  		return
   794  	}
   795  
   796  	// some structs return as an interface
   797  	if data.Kind() == reflect.Interface {
   798  		data = reflect.ValueOf(data.Interface())
   799  	}
   800  	if data = utils.IndirectValue(data); !data.IsValid() {
   801  		return
   802  	}
   803  
   804  	return data.Interface()
   805  }
   806  
   807  type routerOption struct {
   808  	parseFrom      parseFrom
   809  	beforeHandlers []routerHandler
   810  	aftersHandlers []routerHandler
   811  }
   812  
   813  func ParseFromBody() utils.OptionFunc[routerOption] {
   814  	return func(r *routerOption) {
   815  		r.parseFrom = parseFromBody
   816  	}
   817  }
   818  
   819  func ParseFromQuery() utils.OptionFunc[routerOption] {
   820  	return func(r *routerOption) {
   821  		r.parseFrom = parseFromQuery
   822  	}
   823  }
   824  
   825  func HandleBefore(beforeHandlers ...routerHandler) utils.OptionFunc[routerOption] {
   826  	return func(o *routerOption) {
   827  		o.beforeHandlers = beforeHandlers
   828  	}
   829  }
   830  
   831  func HandleAfter(aftersHandlers ...routerHandler) utils.OptionFunc[routerOption] {
   832  	return func(o *routerOption) {
   833  		o.aftersHandlers = aftersHandlers
   834  	}
   835  }