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 }