github.com/aldelo/common@v1.5.1/wrapper/gin/gin.go (about) 1 package gin 2 3 /* 4 * Copyright 2020-2023 Aldelo, LP 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 import ( 20 "database/sql" 21 "fmt" 22 util "github.com/aldelo/common" 23 "github.com/aldelo/common/wrapper/gin/ginbindtype" 24 "github.com/aldelo/common/wrapper/gin/gingzipcompression" 25 "github.com/aldelo/common/wrapper/gin/ginhttpmethod" 26 "github.com/aldelo/common/wrapper/xray" 27 "github.com/gin-contrib/cors" 28 "github.com/gin-contrib/gzip" 29 "github.com/gin-contrib/sessions" 30 "github.com/gin-contrib/sessions/cookie" 31 "github.com/gin-contrib/sessions/redis" 32 "github.com/gin-gonic/gin" 33 "github.com/gin-gonic/gin/binding" 34 "github.com/patrickmn/go-cache" 35 "github.com/utrack/gin-csrf" 36 "golang.org/x/time/rate" 37 "log" 38 "reflect" 39 "strings" 40 "time" 41 ) 42 43 // Gin struct provides a wrapper for gin-gonic based web server operations 44 // 45 // Name = (required) web server descriptive display name 46 // Port = (required) tcp port that this web server will run on 47 // TlsCertPemFile / TlsCertKeyFile = (optional) when both are set, web server runs secured mode using tls cert; path to pem and key file 48 // Routes = (required) map of http route handlers to be registered, middleware to be configured, 49 // 50 // for gin engine or route groups, 51 // key = * indicates base gin engine routes, otherwise key refers to routeGroup to be created 52 // 53 // SessionMiddleware = (optional) defines the cookie or redis session middleware to setup for the gin engine 54 // CsrfMiddleware = (optional) defines the csrf middleware to setup for the gin engine (requires SessionMiddleware setup) 55 // 56 // AWS-XRay Tracing = Passing Parent SegmentID and TraceID to Handler via Gin Context 57 // 58 // Using Headers: 59 // "X-Amzn-Seg-Id" = Parent XRay Segment ID to pass into Headers 60 // "X-Amzn-Tr-Id" = Parent XRay Trace ID to pass into Headers 61 type Gin struct { 62 // web server descriptive name (used for display and logging only) 63 Name string 64 65 // web server port to run gin web server 66 Port uint 67 68 // web server tls certificate pem and key file path 69 TlsCertPemFile string 70 TlsCertKeyFile string 71 72 // google recaptcha v2 secret 73 GoogleRecaptchaSecret string 74 75 // web server routes to handle 76 // string = routeGroup path if defined, otherwise, if * refers to base 77 Routes map[string]*RouteDefinition 78 79 // define the session middleware for the gin engine 80 SessionMiddleware *SessionConfig 81 82 // define the csrf middleware for the gin engine 83 CsrfMiddleware *CsrfConfig 84 85 // define html template renderer 86 HtmlTemplateRenderer *GinTemplate 87 88 // define http status error handler 89 HttpStatusErrorHandler func(status int, trace string, c *gin.Context) 90 91 // web server instance 92 _ginEngine *gin.Engine 93 _ginJwtAuth *GinJwt 94 _limiterCache *cache.Cache 95 } 96 97 // RouteDefinition struct contains per route group or gin engine's handlers and middleware 98 // 99 // Note: 100 // 1. route definition's map key = * means gin engine; key named refers to Route Group 101 // 102 // Routes = (required) one or more route handlers defined for current route group or base engine 103 // CorsMiddleware = (optional) current cors middleware to use if setup for current route group or base engine 104 // MaxLimitMiddleware = (optional) current max rate limit middleware, controls how many concurrent handlers can process actions for the current route group or base engine 105 // PerClientQpsMiddleware = (optional) to enable per client Ip QPS limiter middleware, all 3 options must be set 106 // UseAuthMiddleware = (optional) to indicate if this route group uses auth middleware 107 // CustomMiddleware = (optional) slice of additional custom HandleFunc middleware 108 type RouteDefinition struct { 109 Routes []*Route 110 111 CorsMiddleware *cors.Config 112 MaxLimitMiddleware *int 113 PerClientQpsMiddleware *PerClientQps 114 GZipMiddleware *GZipConfig 115 UseAuthMiddleware bool 116 CustomMiddleware []gin.HandlerFunc 117 } 118 119 // Route struct defines each http route handler endpoint 120 // 121 // RelativePath = (required) route path such as /HelloWorld, this is the path to trigger the route handler 122 // Method = (required) GET, POST, PUT, DELETE 123 // Binding = (optional) various input data binding to target type option 124 // BindingInputPtr = (conditional) binding input object pointer, required if binding type is set 125 // Handler = (required) function handler to be executed per method defined (actual logic goes inside handler) 126 // 1. gin.Context Value Return Helpers: 127 // a) c.String(), c.HTML(), c.JSON(), c.JSONP(), c.PureJSON(), c.AsciiJSON(), c.SecureJSON(), c.XML(), c.YAML(), 128 // c.ProtoBuf(), c.Redirect(), c.Data(), c.DataFromReader(), c.Render() 129 type Route struct { 130 // relative path to the endpoint for the route to handle 131 RelativePath string 132 133 // http method such as GET, POST, PUT, DELETE 134 Method ginhttpmethod.GinHttpMethod 135 136 // input value binding to be performed 137 Binding ginbindtype.GinBindType 138 139 // binding input object pointer 140 BindingInputPtr interface{} 141 142 // actual handler method to be triggered, 143 // bindingInputPtr if any, is the binding resolved object passed into the handler method 144 Handler func(c *gin.Context, bindingInputPtr interface{}) 145 } 146 147 // PerClientQps defines options for the PerClientQps based rate limit middleware 148 type PerClientQps struct { 149 Qps int 150 Burst int 151 TTL time.Duration 152 } 153 154 // GZipConfig defines options for the GZip middleware 155 type GZipConfig struct { 156 Compression gingzipcompression.GinGZipCompression 157 ExcludedExtensions []string 158 ExcludedPaths []string 159 ExcludedPathsRegex []string 160 } 161 162 // GetGZipCompression returns gzip compression value 163 func (gz *GZipConfig) GetGZipCompression() int { 164 switch gz.Compression { 165 case gingzipcompression.Default: 166 return gzip.DefaultCompression 167 case gingzipcompression.BestCompression: 168 return gzip.BestCompression 169 case gingzipcompression.BestSpeed: 170 return gzip.BestSpeed 171 default: 172 return gzip.NoCompression 173 } 174 } 175 176 // GetGZipExcludedExtensions return gzip option for excluded extensions if any 177 func (gz *GZipConfig) GetGZipExcludedExtensions() gzip.Option { 178 if len(gz.ExcludedExtensions) > 0 { 179 return gzip.WithExcludedExtensions(gz.ExcludedExtensions) 180 } else { 181 return nil 182 } 183 } 184 185 // GetGZipExcludedPaths return gzip option for excluded paths if any 186 func (gz *GZipConfig) GetGZipExcludedPaths() gzip.Option { 187 if len(gz.ExcludedPaths) > 0 { 188 return gzip.WithExcludedPaths(gz.ExcludedPaths) 189 } else { 190 return nil 191 } 192 } 193 194 // GetGZipExcludedPathsRegex return gzip option for excluded paths refex if any 195 func (gz *GZipConfig) GetGZipExcludedPathsRegex() gzip.Option { 196 if len(gz.ExcludedPathsRegex) > 0 { 197 return gzip.WithExcludedPathsRegexs(gz.ExcludedPathsRegex) 198 } else { 199 return nil 200 } 201 } 202 203 // SessionConfig defines redis or cookie session configuration options 204 // 205 // SecretKey = (required) for redis or cookie session, the secret key to use 206 // SessionNames = (required) for redis or cookie session, defined session names to use by middleware 207 // RedisMaxIdleConnections = (optional) for redis session, maximum number of idle connections to keep for redis client 208 // RedisHostAndPort = (optional) the redis endpoint host name and port, in host:port format (if not set, then cookie session is assumed) 209 // 210 // To Use Sessions in Handler: 211 // 212 // [Single Session] 213 // session := sessions.Default(c) 214 // v := session.Get("xyz") 215 // session.Set("xyz", xyz) 216 // session.Save() 217 // [Multiple Sessions] 218 // sessionA := sessions.DefaultMany(c, "a") 219 // sessionB := sessions.DefaultMany(c, "b") 220 // sessionA.Get("xyz") 221 // sessionA.Set("xyz", xyz) 222 // sessionA.Save() 223 type SessionConfig struct { 224 SecretKey string 225 SessionNames []string 226 RedisMaxIdleConnections int 227 RedisHostAndPort string 228 } 229 230 // CsrfConfig defines csrf protection middleware options 231 // 232 // Secret = (required) csrf secret key used for csrf protection 233 // ErrorFunc = (required) csrf invalid token error handler 234 // TokenGetter = (optional) csrf get token action override from default (in case implementation not using default keys) 235 // 236 // default gets token from: 237 // - FormValue("_csrf") 238 // - Url.Query().Get("_csrf") 239 // - Header.Get("X-CSRF-TOKEN") 240 // - Header.Get("X-XSRF-TOKEN") 241 type CsrfConfig struct { 242 Secret string 243 ErrorFunc func(c *gin.Context) 244 TokenGetter func(c *gin.Context) string 245 } 246 247 // NewServer returns a gin-gongic web server wrapper ready for setup 248 // 249 // customRecovery = indicates if the gin engine default recovery will be replaced, with one that has more custom render 250 // customHttpErrorHandler = func to custom handle http error 251 // 252 // if gin default logger is to be replaced, it must be replaced via zaplogger parameter, 253 // zaplogger must be fully setup and passed into NewServer in order for zaplogger replacement to be effective, 254 // zaplogger will not be setup after gin object is created 255 func NewServer(name string, port uint, releaseMode bool, customRecovery bool, customHttpErrorHandler func(status int, trace string, c *gin.Context), zaplogger ...*GinZap) *Gin { 256 var z *GinZap 257 258 if len(zaplogger) > 0 { 259 z = zaplogger[0] 260 } 261 262 mode := gin.ReleaseMode 263 264 if !releaseMode { 265 mode = gin.DebugMode 266 } 267 268 gin.SetMode(mode) 269 270 gw := &Gin{ 271 Name: name, 272 Port: port, 273 HttpStatusErrorHandler: customHttpErrorHandler, 274 _ginEngine: nil, 275 _ginJwtAuth: nil, 276 _limiterCache: cache.New(5*time.Minute, 10*time.Minute), 277 } 278 279 if z != nil && util.LenTrim(z.LogName) > 0 { 280 if err := z.Init(); err == nil { 281 gw._ginEngine = gin.New() 282 gw._ginEngine.Use(z.NormalLogger()) 283 gw._ginEngine.Use(z.PanicLogger()) 284 log.Println("Using Zap Logger...") 285 } else { 286 if customRecovery { 287 gw._ginEngine = gin.New() 288 gw._ginEngine.Use(gin.Logger()) 289 gw._ginEngine.Use(NiceRecovery(func(c *gin.Context, err interface{}) { 290 if gw.HttpStatusErrorHandler == nil { 291 c.String(500, err.(error).Error()) 292 } else { 293 gw.HttpStatusErrorHandler(500, err.(error).Error(), c) 294 } 295 })) 296 297 log.Println("Using Custom Recovery...") 298 } else { 299 gw._ginEngine = gin.Default() 300 log.Println("Using Default Recovery, Logger...") 301 } 302 } 303 } else { 304 if customRecovery { 305 gw._ginEngine = gin.New() 306 gw._ginEngine.Use(gin.Logger()) 307 gw._ginEngine.Use(NiceRecovery(func(c *gin.Context, err interface{}) { 308 if gw.HttpStatusErrorHandler == nil { 309 c.String(500, err.(error).Error()) 310 } else { 311 gw.HttpStatusErrorHandler(500, err.(error).Error(), c) 312 } 313 })) 314 log.Println("Using Custom Recovery...") 315 } else { 316 gw._ginEngine = gin.Default() 317 log.Println("Using Default Recovery, Logger...") 318 } 319 } 320 321 return gw 322 } 323 324 // NewAuthMiddleware will create a new jwt auth middleware with basic info provided, 325 // then this new middleware is set into Gin wrapper internal var, 326 // this middleware additional fields and handlers must then be defined by accessing the AuthMiddleware func, 327 // once middleware completely prepared, then call the RunServer which automatically builds the auth middleware for use 328 func (g *Gin) NewAuthMiddleware(realm string, identityKey string, signingSecretKey string, authenticateBinding ginbindtype.GinBindType, setup ...func(j *GinJwt)) bool { 329 g._ginJwtAuth = nil 330 331 if g._ginEngine == nil { 332 return false 333 } 334 335 g._ginJwtAuth = NewGinJwtMiddleware(realm, identityKey, signingSecretKey, authenticateBinding) 336 337 if len(setup) > 0 { 338 setup[0](g._ginJwtAuth) 339 } 340 341 return true 342 } 343 344 // AuthMiddleware returns the GinJwt struct object built by NewAuthMiddleware, 345 // prepare the necessary field values and handlers via this method's return object access 346 func (g *Gin) AuthMiddleware() *GinJwt { 347 return g._ginJwtAuth 348 } 349 350 // ExtractJwtClaims will extra jwt claims from context and return via map 351 func (g *Gin) ExtractJwtClaims(c *gin.Context) map[string]interface{} { 352 if g._ginJwtAuth != nil { 353 return g._ginJwtAuth.ExtractClaims(c) 354 } else { 355 return nil 356 } 357 } 358 359 // Engine represents the gin engine itself 360 func (g *Gin) Engine() *gin.Engine { 361 if g._ginEngine == nil { 362 return nil 363 } else { 364 return g._ginEngine 365 } 366 } 367 368 // RunServer starts gin-gonic web server, 369 // method will run in blocking mode, until gin server exits, 370 // if run server failed, an error is returned 371 func (g *Gin) RunServer() error { 372 if g._ginEngine == nil { 373 return fmt.Errorf("Run Web Server Failed: %s", "Server Engine Not Defined") 374 } 375 376 if util.LenTrim(g.Name) == 0 { 377 return fmt.Errorf("Run Web Server Failed: %s", "Web Server Name Not Defined") 378 } 379 380 if g.Port > 65535 { 381 return fmt.Errorf("Run Web Server Failed: %s", "Port Number Cannot Exceed 65535") 382 } 383 384 // setup html template renderer 385 if g.HtmlTemplateRenderer != nil { 386 g.setupHtmlTemplateRenderer() 387 } 388 389 // setup auth middleware 390 if g._ginJwtAuth != nil { 391 if err := g._ginJwtAuth.BuildGinJwtMiddleware(g); err != nil { 392 return fmt.Errorf("Run Web Server Failed: (%s) %s", "Build Auth Middleware Errored", err.Error()) 393 } 394 } 395 396 // setup routes 397 if g.setupRoutes() <= 0 { 398 return fmt.Errorf("Run Web Server Failed: %s", "Http Routes Not Defined") 399 } 400 401 log.Println("Web Server '" + g.Name + "' Started..." + util.GetLocalIP() + ":" + util.UintToStr(g.Port)) 402 403 var err error 404 405 if util.LenTrim(g.TlsCertPemFile) > 0 && util.LenTrim(g.TlsCertKeyFile) > 0 { 406 // gin on tls 407 log.Println("Web Server Tls Mode") 408 err = g._ginEngine.RunTLS(fmt.Sprintf(":%d", g.Port), g.TlsCertPemFile, g.TlsCertKeyFile) 409 } else { 410 // gin on non-tls 411 log.Println("Web Server Non-Tls Mode") 412 err = g._ginEngine.Run(fmt.Sprintf(":%d", g.Port)) 413 } 414 415 if err != nil { 416 return fmt.Errorf("Web Server '" + g.Name + "' Failed To Start: " + err.Error()) 417 } else { 418 log.Println("Web Server '" + g.Name + "' Stopped") 419 return nil 420 } 421 } 422 423 // BindPostForm will bind the post form data to outputPtr based on given tag names mapping 424 func (g *Gin) BindPostForm(outputPtr interface{}, tagName string, c *gin.Context) error { 425 if outputPtr == nil { 426 return fmt.Errorf("BindPostForm Requires Output Variable Pointer") 427 } 428 429 if util.LenTrim(tagName) == 0 { 430 return fmt.Errorf("BindPostForm Requires TagName") 431 } 432 433 if c == nil { 434 return fmt.Errorf("BindPostForm Requires Gin Context") 435 } 436 437 s := reflect.ValueOf(outputPtr).Elem() 438 439 if s.Kind() != reflect.Struct { 440 return fmt.Errorf("BindPostForm Requires Struct") 441 } 442 443 for i := 0; i < s.NumField(); i++ { 444 field := s.Type().Field(i) 445 446 if o := s.FieldByName(field.Name); o.IsValid() && o.CanSet() { 447 if tag := field.Tag.Get(tagName); util.LenTrim(tag) > 0 { 448 if v := c.PostForm(tag); util.LenTrim(v) > 0 { 449 switch o.Kind() { 450 case reflect.String: 451 o.SetString(v) 452 case reflect.Bool: 453 b, _ := util.ParseBool(v) 454 o.SetBool(b) 455 case reflect.Int8: 456 fallthrough 457 case reflect.Int16: 458 fallthrough 459 case reflect.Int: 460 fallthrough 461 case reflect.Int32: 462 fallthrough 463 case reflect.Int64: 464 if i64, ok := util.ParseInt64(v); ok { 465 if !o.OverflowInt(i64) { 466 o.SetInt(i64) 467 } 468 } 469 case reflect.Float32: 470 fallthrough 471 case reflect.Float64: 472 if f64, ok := util.ParseFloat64(v); ok { 473 if !o.OverflowFloat(f64) { 474 o.SetFloat(f64) 475 } 476 } 477 case reflect.Uint8: 478 fallthrough 479 case reflect.Uint16: 480 fallthrough 481 case reflect.Uint: 482 fallthrough 483 case reflect.Uint32: 484 fallthrough 485 case reflect.Uint64: 486 ui := uint64(util.StrToUint(v)) 487 if !o.OverflowUint(ui) { 488 o.SetUint(ui) 489 } 490 default: 491 switch f := o.Interface().(type) { 492 case sql.NullString: 493 f = util.ToNullString(v, true) 494 o.Set(reflect.ValueOf(f)) 495 case sql.NullBool: 496 b, _ := util.ParseBool(v) 497 f = util.ToNullBool(b) 498 o.Set(reflect.ValueOf(f)) 499 case sql.NullFloat64: 500 f64, _ := util.ParseFloat64(v) 501 f = util.ToNullFloat64(f64, true) 502 o.Set(reflect.ValueOf(f)) 503 case sql.NullInt32: 504 i32, _ := util.ParseInt32(v) 505 f = util.ToNullInt(i32, true) 506 o.Set(reflect.ValueOf(f)) 507 case sql.NullInt64: 508 i64, _ := util.ParseInt64(v) 509 f = util.ToNullInt64(i64, true) 510 o.Set(reflect.ValueOf(f)) 511 case sql.NullTime: 512 f = util.ToNullTime(util.ParseDateTime(v)) 513 o.Set(reflect.ValueOf(f)) 514 case time.Time: 515 f = util.ParseDateTime(v) 516 o.Set(reflect.ValueOf(f)) 517 default: 518 return fmt.Errorf("BindPostForm Encountered Unhandled Field Type: %s", o.Kind().String()) 519 } 520 } 521 } 522 } 523 } 524 } 525 526 return nil 527 } 528 529 // bindInput will attempt to bind input data to target binding output, for example json string to struct mapped to json elements 530 // 531 // bindObjPtr = pointer to the target object, cannot be nil 532 func (g *Gin) bindInput(c *gin.Context, bindType ginbindtype.GinBindType, bindObjPtr interface{}) (err error) { 533 if c == nil { 534 return fmt.Errorf("Binding Context is Nil") 535 } 536 537 if !bindType.Valid() { 538 return fmt.Errorf("Binding Type Not Valid") 539 } 540 541 if bindObjPtr == nil { 542 return fmt.Errorf("Binding Target Object Pointer Not Defined") 543 } 544 545 switch bindType { 546 case ginbindtype.BindHeader: 547 err = c.ShouldBindHeader(bindObjPtr) 548 case ginbindtype.BindJson: 549 err = c.ShouldBindJSON(bindObjPtr) 550 case ginbindtype.BindProtoBuf: 551 err = c.ShouldBindWith(bindObjPtr, binding.ProtoBuf) 552 case ginbindtype.BindQuery: 553 err = c.ShouldBindQuery(bindObjPtr) 554 case ginbindtype.BindUri: 555 err = c.ShouldBindUri(bindObjPtr) 556 case ginbindtype.BindXml: 557 err = c.ShouldBindXML(bindObjPtr) 558 case ginbindtype.BindYaml: 559 err = c.ShouldBindYAML(bindObjPtr) 560 case ginbindtype.BindPostForm: 561 err = g.BindPostForm(bindObjPtr, "json", c) 562 default: 563 err = c.ShouldBind(bindObjPtr) 564 } 565 566 if err != nil { 567 log.Println("Bind Error:", err.Error(), "; Bind Type:", bindType.Key()) 568 return err 569 } 570 571 return nil 572 } 573 574 // setupRoutes prepares gin engine with route handlers, middleware and etc. 575 func (g *Gin) setupRoutes() int { 576 if g.Routes == nil { 577 return 0 578 } 579 580 count := 0 581 582 // setup health check route 583 g._ginEngine.GET("/health", func(c *gin.Context) { 584 c.String(200, "OK") 585 }) 586 587 if g.HttpStatusErrorHandler != nil { 588 g._ginEngine.NoRoute(func(context *gin.Context) { 589 g.HttpStatusErrorHandler(404, "NoRoute", context) 590 }) 591 592 g._ginEngine.NoMethod(func(context *gin.Context) { 593 g.HttpStatusErrorHandler(404, "NoMethod", context) 594 }) 595 } 596 597 // setup xray trace middleware 598 if xray.XRayServiceOn() { 599 g._ginEngine.Use(XRayMiddleware()) 600 } 601 602 // setup session if configured 603 if g.SessionMiddleware != nil { 604 g.setupSessionMiddleware() 605 } 606 607 // setup csrf if configured 608 if g.CsrfMiddleware != nil { 609 g.setupCsrfMiddleware() 610 } 611 612 // setup routes for engine and route groups 613 for k, v := range g.Routes { 614 var rg *gin.RouterGroup 615 616 if k != "*" && k != "base" { 617 // route group 618 rg = g._ginEngine.Group(k) 619 } 620 621 routeFn := func() gin.IRoutes { 622 if rg == nil { 623 return g._ginEngine 624 } else { 625 return rg 626 } 627 } 628 629 if v == nil { 630 continue 631 } 632 633 // 634 // config any middleware first 635 // 636 if v.CorsMiddleware != nil && !v.CorsMiddleware.AllowAllOrigins && len(v.CorsMiddleware.AllowOrigins) > 0 { 637 g.setupCorsMiddleware(routeFn(), v.CorsMiddleware) 638 } 639 640 if v.MaxLimitMiddleware != nil && *v.MaxLimitMiddleware > 0 { 641 g.setupMaxLimitMiddleware(routeFn(), *v.MaxLimitMiddleware) 642 } 643 644 if v.PerClientQpsMiddleware != nil { 645 g.setupPerClientIpQpsMiddleware(routeFn(), v.PerClientQpsMiddleware.Qps, v.PerClientQpsMiddleware.Burst, v.PerClientQpsMiddleware.TTL) 646 } 647 648 if v.GZipMiddleware != nil { 649 g.setupGZipMiddleware(routeFn(), v.GZipMiddleware) 650 } 651 652 if v.UseAuthMiddleware && g._ginJwtAuth != nil && g._ginJwtAuth._ginJwtMiddleware != nil { 653 routeFn().Use(g._ginJwtAuth.AuthMiddleware()) 654 log.Println("Using Jwt Auth Middleware...") 655 } 656 657 if len(v.CustomMiddleware) > 0 { 658 routeFn().Use(v.CustomMiddleware...) 659 log.Println("Using Custom Middleware...") 660 } 661 662 // 663 // setup route handlers 664 // 665 for _, h := range v.Routes { 666 log.Println("Setting Up Route Handler: " + h.RelativePath) 667 668 if !h.Method.Valid() || h.Method == ginhttpmethod.UNKNOWN { 669 continue 670 } 671 672 if !h.Binding.Valid() { 673 continue 674 } 675 676 if util.LenTrim(h.RelativePath) == 0 { 677 continue 678 } 679 680 if h.Handler == nil { 681 continue 682 } 683 684 // add route 685 switch h.Method { 686 case ginhttpmethod.GET: 687 routeFn().GET(h.RelativePath, g.newRouteFunc(h.RelativePath, h.Method.Key(), h.Binding, h.BindingInputPtr, h.Handler)) 688 689 case ginhttpmethod.POST: 690 routeFn().POST(h.RelativePath, g.newRouteFunc(h.RelativePath, h.Method.Key(), h.Binding, h.BindingInputPtr, h.Handler)) 691 692 case ginhttpmethod.PUT: 693 routeFn().PUT(h.RelativePath, g.newRouteFunc(h.RelativePath, h.Method.Key(), h.Binding, h.BindingInputPtr, h.Handler)) 694 695 case ginhttpmethod.DELETE: 696 routeFn().DELETE(h.RelativePath, g.newRouteFunc(h.RelativePath, h.Method.Key(), h.Binding, h.BindingInputPtr, h.Handler)) 697 698 default: 699 continue 700 } 701 702 // add handler counter 703 count++ 704 } 705 } 706 707 return count 708 } 709 710 // newRouteFunc returns closure to route handler setup, 711 // if we define the route handler within the loop in Route Setup, the handler func were reused (not desired effect), 712 // however, using closure ensures each relative path uses its own route func 713 func (g *Gin) newRouteFunc(relativePath string, method string, bindingType ginbindtype.GinBindType, bindingInputPtr interface{}, 714 handler func(c *gin.Context, bindingInputPtr interface{})) func(context *gin.Context) { 715 return func(c *gin.Context) { 716 if util.LenTrim(g.GoogleRecaptchaSecret) > 0 { 717 c.Set("google_recaptcha_secret", g.GoogleRecaptchaSecret) 718 } 719 720 if bindingInputPtr != nil { 721 // will perform binding 722 // create new object of bindingInputPtr 723 if t := util.ReflectGetType(bindingInputPtr); t != nil { 724 bindingInputPtr = util.ReflectObjectNewPtr(t) 725 726 if bindingInputPtr == nil { 727 _ = c.AbortWithError(500, fmt.Errorf("%s %s Failed on %s Binding: %s", method, relativePath, bindingType.Key(), "ReflectObjectNewPtr() Yielded Nil for Target Binding Object")) 728 handler(c, nil) 729 return 730 } 731 } 732 733 if err := g.bindInput(c, bindingType, bindingInputPtr); err != nil { 734 // binding error 735 _ = c.AbortWithError(500, fmt.Errorf("%s %s Failed on %s Binding: %s", method, relativePath, bindingType.Key(), err.Error())) 736 handler(c, nil) 737 } else { 738 // continue processing 739 handler(c, bindingInputPtr) 740 } 741 } else { 742 // no binding requested 743 handler(c, nil) 744 } 745 } 746 } 747 748 // setupCorsMiddleware is a helper to setup gin middleware 749 func (g *Gin) setupCorsMiddleware(rg gin.IRoutes, corsConfig *cors.Config) { 750 if rg != nil && corsConfig != nil { 751 config := cors.DefaultConfig() 752 753 if len(corsConfig.AllowOrigins) > 0 { 754 orig := []string{} 755 756 for _, v := range corsConfig.AllowOrigins { 757 if util.LenTrim(v) > 0 && strings.ToLower(util.Left(v, 4)) == "http" { 758 orig = append(orig, v) 759 } 760 } 761 762 if len(orig) > 0 { 763 config.AllowOrigins = orig 764 } 765 } 766 767 if len(corsConfig.AllowMethods) > 0 { 768 method := []string{} 769 770 for _, v := range corsConfig.AllowMethods { 771 if util.LenTrim(v) > 0 { 772 method = append(method, v) 773 } 774 } 775 776 if len(method) > 0 { 777 config.AllowMethods = method 778 } 779 } 780 781 if len(corsConfig.AllowHeaders) > 0 { 782 header := []string{} 783 784 for _, v := range corsConfig.AllowHeaders { 785 if util.LenTrim(v) > 0 { 786 header = append(header, v) 787 } 788 } 789 790 if len(header) > 0 { 791 config.AllowHeaders = header 792 } 793 } 794 795 if len(corsConfig.ExposeHeaders) > 0 { 796 header := []string{} 797 798 for _, v := range corsConfig.ExposeHeaders { 799 if util.LenTrim(v) > 0 { 800 header = append(header, v) 801 } 802 } 803 804 if len(header) > 0 { 805 config.ExposeHeaders = header 806 } 807 } 808 809 if corsConfig.AllowOriginFunc != nil { 810 config.AllowOriginFunc = corsConfig.AllowOriginFunc 811 } 812 813 if corsConfig.MaxAge > 0 { 814 config.MaxAge = corsConfig.MaxAge 815 } 816 817 config.AllowAllOrigins = corsConfig.AllowAllOrigins 818 config.AllowBrowserExtensions = corsConfig.AllowBrowserExtensions 819 config.AllowCredentials = corsConfig.AllowCredentials 820 config.AllowFiles = corsConfig.AllowFiles 821 config.AllowWebSockets = corsConfig.AllowWebSockets 822 config.AllowWildcard = corsConfig.AllowWildcard 823 824 if !config.AllowAllOrigins { 825 if len(config.AllowOrigins) == 0 { 826 config.AllowAllOrigins = true 827 } 828 } 829 830 rg.Use(cors.New(config)) 831 832 log.Println("Using Cors Middleware...") 833 } 834 } 835 836 // setupMaxLimitMiddleware sets up max concurrent handler execution rate limiter middleware 837 func (g *Gin) setupMaxLimitMiddleware(rg gin.IRoutes, maxLimit int) { 838 if rg != nil && maxLimit > 0 { 839 rg.Use(func() gin.HandlerFunc { 840 s := make(chan struct{}, maxLimit) 841 842 acquire := func() { 843 s <- struct{}{} 844 } 845 846 release := func() { 847 <-s 848 } 849 850 return func(c *gin.Context) { 851 acquire() 852 defer release() 853 c.Next() 854 } 855 }()) 856 857 log.Println("Using Max Limit Middleware...") 858 } 859 } 860 861 // setupPerClientIpQpsMiddleware sets up per client ip qps limiter middleware 862 func (g *Gin) setupPerClientIpQpsMiddleware(rg gin.IRoutes, qps int, burst int, ttl time.Duration) { 863 if rg != nil && g._limiterCache != nil && qps > 0 && qps <= 1000000 && burst > 0 && ttl > 0 { 864 fn := func(key func(c *gin.Context) string, 865 createLimiter func(c *gin.Context) (*rate.Limiter, time.Duration), 866 abort func(c *gin.Context)) gin.HandlerFunc { 867 return func(cc *gin.Context) { 868 k := key(cc) 869 limiter, ok := g._limiterCache.Get(k) 870 871 if !ok { 872 var expire time.Duration 873 limiter, expire = createLimiter(cc) 874 g._limiterCache.Set(k, limiter, expire) 875 } 876 877 ok = limiter.(*rate.Limiter).Allow() 878 879 if !ok { 880 abort(cc) 881 return 882 } 883 884 cc.Next() 885 } 886 } 887 888 rg.Use(fn(func(c *gin.Context) string { 889 return c.ClientIP() 890 }, func(c *gin.Context) (*rate.Limiter, time.Duration) { 891 n := 1000000 / qps 892 return rate.NewLimiter(rate.Every(time.Duration(n)*time.Microsecond), burst), ttl 893 }, func(c *gin.Context) { 894 c.AbortWithStatus(429) // exceed rate limit request 895 })) // code based on github.com/yangxikun/gin-limit-by-key 896 897 log.Println("Using Per Client Ip Qps Middleware...") 898 } 899 } 900 901 // setupGZipMiddleware sets up GZip middleware 902 func (g *Gin) setupGZipMiddleware(rg gin.IRoutes, gz *GZipConfig) { 903 if gz != nil && gz.Compression.Valid() && gz.Compression != gingzipcompression.UNKNOWN { 904 c := gz.GetGZipCompression() 905 906 exts := gz.GetGZipExcludedExtensions() 907 paths := gz.GetGZipExcludedPaths() 908 pregex := gz.GetGZipExcludedPathsRegex() 909 910 opts := []gzip.Option{} 911 912 if exts != nil { 913 opts = append(opts, exts) 914 } 915 916 if paths != nil { 917 opts = append(opts, paths) 918 } 919 920 if pregex != nil { 921 opts = append(opts, pregex) 922 } 923 924 if len(opts) > 0 { 925 rg.Use(gzip.Gzip(c, opts...)) 926 } else { 927 rg.Use(gzip.Gzip(c)) 928 } 929 930 log.Println("Using GZip Middleware...") 931 } 932 } 933 934 // setupSessionMiddleware sets up session middleware, 935 // session is set up on the gin engine level (rather than route groups) 936 func (g *Gin) setupSessionMiddleware() { 937 if g._ginEngine != nil && g.SessionMiddleware != nil && util.LenTrim(g.SessionMiddleware.SecretKey) > 0 && len(g.SessionMiddleware.SessionNames) > 0 { 938 var store sessions.Store 939 940 if util.LenTrim(g.SessionMiddleware.RedisHostAndPort) == 0 { 941 // cookie store 942 store = cookie.NewStore([]byte(g.SessionMiddleware.SecretKey)) 943 } else { 944 // redis store 945 size := g.SessionMiddleware.RedisMaxIdleConnections 946 947 if size <= 0 { 948 size = 1 949 } 950 951 store, _ = redis.NewStore(size, "tcp", g.SessionMiddleware.RedisHostAndPort, "", []byte(g.SessionMiddleware.SecretKey)) 952 } 953 954 if store != nil { 955 if len(g.SessionMiddleware.SessionNames) == 1 { 956 g._ginEngine.Use(sessions.Sessions(g.SessionMiddleware.SessionNames[0], store)) 957 } else { 958 g._ginEngine.Use(sessions.SessionsMany(g.SessionMiddleware.SessionNames, store)) 959 } 960 961 log.Println("Using Session Middleware...") 962 } 963 } 964 } 965 966 // setupCsrfMiddleware sets up csrf protection middleware, 967 // this middleware is set up on the gin engine level (rather than route groups), 968 // this middleware requires gin-contrib/sessions middleware setup and used before setting this up 969 func (g *Gin) setupCsrfMiddleware() { 970 if g._ginEngine != nil && g.SessionMiddleware != nil && g.CsrfMiddleware != nil && util.LenTrim(g.SessionMiddleware.SecretKey) > 0 && len(g.SessionMiddleware.SessionNames) > 0 && util.LenTrim(g.CsrfMiddleware.Secret) > 0 { 971 opt := csrf.Options{ 972 Secret: g.CsrfMiddleware.Secret, 973 } 974 975 if g.CsrfMiddleware.ErrorFunc != nil { 976 opt.ErrorFunc = g.CsrfMiddleware.ErrorFunc 977 } else { 978 opt.ErrorFunc = func(c *gin.Context) { 979 c.String(400, "CSRF Token Mismatch") 980 c.Abort() 981 } 982 } 983 984 if g.CsrfMiddleware.TokenGetter != nil { 985 opt.TokenGetter = g.CsrfMiddleware.TokenGetter 986 } 987 988 g._ginEngine.Use(csrf.Middleware(opt)) 989 log.Println("Using Csrf Middleware...") 990 } 991 } 992 993 // setupHtmlTemplateRenderer sets up html template renderer with gin engine 994 func (g *Gin) setupHtmlTemplateRenderer() { 995 if g.HtmlTemplateRenderer != nil { 996 if err := g.HtmlTemplateRenderer.LoadHtmlTemplates(); err != nil { 997 log.Println("Load Html Template Renderer Failed: " + err.Error()) 998 return 999 } 1000 1001 if err := g.HtmlTemplateRenderer.SetHtmlRenderer(g); err != nil { 1002 log.Println("Set Html Template Renderer Failed: " + err.Error()) 1003 return 1004 } 1005 1006 log.Println("Html Template Renderer Set...") 1007 } 1008 } 1009 1010 /* 1011 1012 // method_descriptions_only lists most of gin's context methods, its method signature, and method descriptions, 1013 // for simpler reference in one place rather having to refer to documentation separately 1014 func method_descriptions_only() { 1015 c := gin.Context{} 1016 1017 // ----------------------------------------------------------------------------------------------------------------- 1018 // http handler return value methods 1019 // ----------------------------------------------------------------------------------------------------------------- 1020 1021 // String writes the given string into the response body. 1022 c.String(code int, format string, values ...interface{}) 1023 1024 // HTML renders the HTTP template specified by its file name. 1025 // It also updates the HTTP code and sets the Content-Type as "text/html". 1026 // See http://golang.org/doc/articles/wiki/ 1027 c.HTML(code int, name string, obj interface{}) 1028 1029 // JSON serializes the given struct as JSON into the response body. 1030 // It also sets the Content-Type as "application/json". 1031 c.JSON(code int, obj interface{}) 1032 1033 // JSONP serializes the given struct as JSON into the response body. 1034 // It add padding to response body to request data from a server residing in a different domain than the client. 1035 // It also sets the Content-Type as "application/javascript". 1036 c.JSONP(code int, obj interface{}) 1037 1038 // PureJSON serializes the given struct as JSON into the response body. 1039 // PureJSON, unlike JSON, does not replace special html characters with their unicode entities. 1040 c.PureJSON(code int, obj interface{}) 1041 1042 // AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. 1043 // It also sets the Content-Type as "application/json". 1044 c.AsciiJSON(code int, obj interface{}) 1045 1046 // SecureJSON serializes the given struct as Secure JSON into the response body. 1047 // Default prepends "while(1)," to response body if the given struct is array values. 1048 // It also sets the Content-Type as "application/json". 1049 c.SecureJSON(code int, obj interface{}) 1050 1051 // XML serializes the given struct as XML into the response body. 1052 // It also sets the Content-Type as "application/xml". 1053 c.XML(code int, obj interface{}) 1054 1055 // YAML serializes the given struct as YAML into the response body. 1056 c.YAML(code int, obj interface{}) 1057 1058 // ProtoBuf serializes the given struct as ProtoBuf into the response body. 1059 c.ProtoBuf(code int, obj interface{}) 1060 1061 // Redirect returns a HTTP redirect to the specific location. 1062 c.Redirect(code int, location string) 1063 1064 // Data writes some data into the body stream and updates the HTTP code. 1065 c.Data(code int, contentType string, data []byte) 1066 1067 // DataFromReader writes the specified reader into the body stream and updates the HTTP code. 1068 c.DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) 1069 1070 // Render writes the response headers and calls render.Render to render data. 1071 c.Render(code int, r render.Render) 1072 1073 // ----------------------------------------------------------------------------------------------------------------- 1074 // request's query-parameter key-value methods 1075 // ----------------------------------------------------------------------------------------------------------------- 1076 1077 // DefaultQuery returns the keyed url query value if it exists, 1078 // otherwise it returns the specified defaultValue string. 1079 // See: Query() and GetQuery() for further information. 1080 // GET /?name=Manu&lastname= 1081 // c.DefaultQuery("name", "unknown") == "Manu" 1082 // c.DefaultQuery("id", "none") == "none" 1083 // c.DefaultQuery("lastname", "none") == "" 1084 c.DefaultQuery(key string, defaultValue string) string 1085 1086 // Query returns the keyed url query value if it exists, 1087 // otherwise it returns an empty string `("")`. 1088 // It is shortcut for `c.Request.URL.Query().Get(key)` 1089 // GET /path?id=1234&name=Manu&value= 1090 // c.Query("id") == "1234" 1091 // c.Query("name") == "Manu" 1092 // c.Query("value") == "" 1093 // c.Query("wtf") == "" 1094 c.Query(key string) string 1095 1096 // QueryArray returns a slice of strings for a given query key. 1097 // The length of the slice depends on the number of params with the given key. 1098 c.QueryArray(key string) []string 1099 1100 // QueryMap returns a map for a given query key. 1101 c.QueryMap(key string) map[string]string 1102 1103 // GetQuery is like Query(), 1104 // it returns the keyed url query value if it exists `(value, true)` (even when the value is an empty string), 1105 // otherwise it returns `("", false)`. It is shortcut for `c.Request.URL.Query().Get(key)` 1106 // GET /?name=Manu&lastname= 1107 // ("Manu", true) == c.GetQuery("name") 1108 // ("", false) == c.GetQuery("id") 1109 // ("", true) == c.GetQuery("lastname") 1110 c.GetQuery(key string) (string, bool) 1111 1112 // GetQueryArray returns a slice of strings for a given query key, 1113 // plus a boolean value whether at least one value exists for the given key. 1114 c.GetQueryArray(key string) ([]string, bool) 1115 1116 // GetQueryMap returns a map for a given query key, 1117 // plus a boolean value whether at least one value exists for the given key. 1118 c.GetQueryMap(key string) (map[string]string, bool) 1119 1120 // ----------------------------------------------------------------------------------------------------------------- 1121 // request's form-post key-value methods 1122 // ----------------------------------------------------------------------------------------------------------------- 1123 1124 // DefaultPostForm returns the specified key from a POST urlencoded form or multipart form when it exists, 1125 // otherwise it returns the specified defaultValue string. 1126 // See: PostForm() and GetPostForm() for further information. 1127 c.DefaultPostForm(key string, defaultValue string) string 1128 1129 // PostForm returns the specified key from a POST urlencoded form or multipart form when it exists, 1130 // otherwise it returns an empty string `("")`. 1131 c.PostForm(key string) string 1132 1133 // PostFormArray returns a slice of strings for a given form key. 1134 // The length of the slice depends on the number of params with the given key. 1135 c.PostFormArray(key string) []string 1136 1137 // PostFormMap returns a map for a given form key. 1138 c.PostFormMap(key string) map[string]string 1139 1140 // GetPostForm is like PostForm(key). 1141 // It returns the specified key from a POST urlencoded form or multipart form when it exists `(value, true)` (even when the value is an empty string), 1142 // otherwise it returns ("", false). For example, during a PATCH request to update the user's email: 1143 // email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com" 1144 // email= --> ("", true) := GetPostForm("email") // set email to "" 1145 // --> ("", false) := GetPostForm("email") // do nothing with email 1146 c.GetPostForm(key string) (string, bool) 1147 1148 // GetPostFormArray returns a slice of strings for a given form key, 1149 // plus a boolean value whether at least one value exists for the given key. 1150 c.GetPostFormArray(key string) ([]string, bool) 1151 1152 // GetPostFormMap returns a map for a given form key, 1153 // plus a boolean value whether at least one value exists for the given key. 1154 c.GetPostFormMap(key string) (map[string]string, bool) 1155 1156 // MultipartForm is the parsed multipart form, including file uploads. 1157 c.MultipartForm() (*multipart.Form, error) 1158 1159 // FormFile returns the first file for the provided form key. 1160 c.FormFile(name string) (*multipart.FileHeader, error) 1161 1162 // ----------------------------------------------------------------------------------------------------------------- 1163 // request abort methods 1164 // ----------------------------------------------------------------------------------------------------------------- 1165 1166 // Abort prevents pending handlers from being called. 1167 // Note that this will not stop the current handler. 1168 // Let's say you have an authorization middleware that validates that the current request is authorized. 1169 // If the authorization fails (ex: the password does not match), 1170 // call Abort to ensure the remaining handlers for this request are not called. 1171 c.Abort() 1172 1173 // AbortWithError calls `AbortWithStatus()` and `Error()` internally. 1174 // This method stops the chain, writes the status code and pushes the specified error to `c.Errors`. 1175 // See Context.Error() for more details. 1176 c.AbortWithError(code int, err error) *Error 1177 1178 // AbortWithStatus calls `Abort()` and writes the headers with the specified status code. 1179 // For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401). 1180 c.AbortWithStatus(code int) 1181 1182 // AbortWithStatusJSON calls `Abort()` and then `JSON` internally. 1183 // This method stops the chain, writes the status code and return a JSON body. 1184 // It also sets the Content-Type as "application/json". 1185 c.AbortWithStatusJSON(code int, jsonObj interface{}) 1186 1187 // ----------------------------------------------------------------------------------------------------------------- 1188 // request and response related objects 1189 // ----------------------------------------------------------------------------------------------------------------- 1190 1191 // access to the underlying http request object 1192 c.Request *http.Request 1193 1194 // access to the underlying http response object 1195 c.Writer ResponseWriter 1196 1197 // access to the context keys object 1198 c.Keys map[string]interface{} 1199 1200 // access to the context params object 1201 c.Params Params 1202 1203 // ----------------------------------------------------------------------------------------------------------------- 1204 // request and response helper methods 1205 // ----------------------------------------------------------------------------------------------------------------- 1206 1207 // ClientIP implements a best effort algorithm to return the real client IP, 1208 // it parses X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy. 1209 // Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP. 1210 c.ClientIP() string 1211 1212 // ContentType returns the Content-Type header of the request. 1213 c.ContentType() string 1214 1215 // Header is a intelligent shortcut for c.Writer.Header().Set(key, value). 1216 // It writes a header in the response. 1217 // If value == "", this method removes the header `c.Writer.Header().Del(key)` 1218 c.Header(key string, value string) 1219 1220 // Cookie returns the named cookie provided in the request or ErrNoCookie if not found. 1221 // And return the named cookie is unescaped. 1222 // If multiple cookies match the given name, only one cookie will be returned. 1223 c.Cookie(name string) (string, error) 1224 1225 // FullPath returns a matched route full path. For not found routes returns an empty string. 1226 // router.GET("/user/:id", func(c *gin.Context) { 1227 // c.FullPath() == "/user/:id" // true 1228 // }) 1229 c.FullPath() string 1230 1231 // SaveUploadedFile uploads the form file to specific dst. 1232 c.SaveUploadedFile(file *multipart.FileHeader, dst string) error 1233 1234 // Status sets the HTTP response code. 1235 c.Status(code int) 1236 1237 // Value returns the value associated with this context for key, 1238 // or nil if no value is associated with key. 1239 // Successive calls to Value with the same key returns the same result. 1240 c.Value(key interface{}) interface{} 1241 1242 // Param returns the value of the URL param. 1243 // It is a shortcut for c.Params.ByName(key) 1244 // router.GET("/user/:id", func(c *gin.Context) { 1245 // // a GET request to /user/john 1246 // id := c.Param("id") // id == "john" 1247 // }) 1248 c.Param(key string) string 1249 1250 // File writes the specified file into the body stream in a efficient way. 1251 c.File(filepath string) 1252 1253 // FileAttachment writes the specified file into the body stream in an efficient way On the client side, 1254 // the file will typically be downloaded with the given filename 1255 c.FileAttachment(filepath string, filename string) 1256 1257 // FileFromFS writes the specified file from http.FileSytem into the body stream in an efficient way. 1258 c.FileFromFS(filepath string, fs http.FileSystem) 1259 1260 // Error attaches an error to the current context. 1261 // The error is pushed to a list of errors. 1262 // It's a good idea to call Error for each error that occurred during the resolution of a request. 1263 // 1264 // A middleware can be used to collect all the errors and push them to a database together, 1265 // print a log, or append it in the HTTP response. 1266 // Error will panic if err is nil. 1267 c.Error(err error) *Error 1268 1269 // ----------------------------------------------------------------------------------------------------------------- 1270 // set helpers 1271 // ----------------------------------------------------------------------------------------------------------------- 1272 1273 // Set is used to store a new key/value pair exclusively for this context. 1274 // It also lazy initializes c.Keys if it was not used previously. 1275 // c.Set(key string, value interface{}) 1276 1277 // SetAccepted sets Accept header data. 1278 c.SetAccepted(formats ...string) 1279 1280 // SetCookie adds a Set-Cookie header to the ResponseWriter's headers. 1281 // The provided cookie must have a valid Name. Invalid cookies may be silently dropped. 1282 c.SetCookie(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool) 1283 1284 // SetSameSite with cookie 1285 c.SetSameSite(samesite http.SameSite) 1286 1287 // ----------------------------------------------------------------------------------------------------------------- 1288 // get helpers 1289 // ----------------------------------------------------------------------------------------------------------------- 1290 1291 // Get returns the value for the given key, ie: (value, true). 1292 // If the value does not exists it returns (nil, false) 1293 c.Get(key string) (value interface{}, exists bool) 1294 1295 // GetHeader returns value from request headers. 1296 c.GetHeader(key string) string 1297 1298 // GetInt returns the value associated with the key as an integer. 1299 c.GetInt(key string) (i int) 1300 1301 // GetInt64 returns the value associated with the key as an integer. 1302 c.GetInt64(key string) (i64 int64) 1303 1304 // GetFloat64 returns the value associated with the key as a float64 1305 c.GetFloat64(key string) (f64 float64) 1306 1307 // GetTime returns the value associated with the key as time. 1308 c.GetTime(key string) (t time.Time) 1309 1310 // GetDuration returns the value associated with the key as a duration. 1311 c.GetDuration(key string) (d time.Duration) 1312 1313 // GetBool returns the value associated with the key as a boolean. 1314 c.GetBool(key string) (b bool) 1315 1316 // GetString returns the value associated with the key as a string. 1317 c.GetString(key string) (s string) 1318 1319 // GetStringMap returns the value associated with the key as a map of interfaces. 1320 c.GetStringMap(key string) (sm map[string]interface{}) 1321 1322 // GetStringMapString returns the value associated with the key as a map of strings. 1323 c.GetStringMapString(key string) (sms map[string]string) 1324 1325 // GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. 1326 c.GetStringMapStringSlice(key string) (smss map[string][]string) 1327 1328 // ----------------------------------------------------------------------------------------------------------------- 1329 // stream helpers 1330 // ----------------------------------------------------------------------------------------------------------------- 1331 1332 // Stream sends a streaming response and returns a boolean indicates "Is client disconnected in middle of stream" 1333 c.Stream(step func(w io.Writer) bool) bool 1334 1335 // GetRawData return stream data. 1336 c.GetRawData() ([]byte, error) 1337 1338 // SSEvent writes a Server-Sent Event into the body stream. 1339 c.SSEvent(name string, message interface{}) 1340 1341 // ----------------------------------------------------------------------------------------------------------------- 1342 // other helpers 1343 // ----------------------------------------------------------------------------------------------------------------- 1344 1345 // Copy returns a copy of the current context that can be safely used outside the request's scope. 1346 // This has to be used when the context has to be passed to a goroutine. 1347 c.Copy() *Context 1348 1349 // IsAborted returns true if the current context was aborted. 1350 c.IsAborted() bool 1351 1352 // IsWebsocket returns true if the request headers 1353 // indicate that a websocket handshake is being initiated by the client. 1354 c.IsWebsocket() bool 1355 1356 // Negotiate calls different Render according acceptable Accept format. 1357 c.Negotiate(code int, config Negotiate) 1358 1359 // NegotiateFormat returns an acceptable Accept format. 1360 c.NegotiateFormat(offered ...string) string 1361 } 1362 1363 */ 1364 1365 /* 1366 Additional Gin Middleware That Can Be Added via CustomMiddleware Slice: 1367 1368 *) Request Response Interceptor / Tracer: 1369 https://github.com/averageflow/goscope 1370 https://github.com/tpkeeper/gin-dump 1371 https://github.com/gin-contrib/opengintracing 1372 *) Prometheus Export: 1373 https://github.com/zsais/go-gin-prometheus 1374 https://github.com/chenjiandongx/ginprom 1375 *) OAuth2: 1376 https://github.com/zalando/gin-oauth2 1377 *) Static Bin 1378 https://github.com/olebedev/staticbin 1379 https://github.com/gin-contrib/static 1380 *) Server Send Event (SSE): 1381 https://github.com/gin-contrib/sse 1382 */