1# Gin Web Framework 2 3<img align="right" width="159px" src="https://raw.githubusercontent.com/gin-gonic/logo/master/color.png"> 4 5[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) 6[![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) 7[![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin) 8[![GoDoc](https://pkg.go.dev/badge/github.com/gin-gonic/gin?status.svg)](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc) 9[![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 10[![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) 11[![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin) 12[![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases) 13[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/gin-gonic/gin)](https://www.tickgit.com/browse?repo=github.com/gin-gonic/gin) 14 15Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin. 16 17 18## Contents 19 20- [Gin Web Framework](#gin-web-framework) 21 - [Contents](#contents) 22 - [Installation](#installation) 23 - [Quick start](#quick-start) 24 - [Benchmarks](#benchmarks) 25 - [Gin v1. stable](#gin-v1-stable) 26 - [Build with jsoniter](#build-with-jsoniter) 27 - [API Examples](#api-examples) 28 - [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options) 29 - [Parameters in path](#parameters-in-path) 30 - [Querystring parameters](#querystring-parameters) 31 - [Multipart/Urlencoded Form](#multiparturlencoded-form) 32 - [Another example: query + post form](#another-example-query--post-form) 33 - [Map as querystring or postform parameters](#map-as-querystring-or-postform-parameters) 34 - [Upload files](#upload-files) 35 - [Single file](#single-file) 36 - [Multiple files](#multiple-files) 37 - [Grouping routes](#grouping-routes) 38 - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default) 39 - [Using middleware](#using-middleware) 40 - [How to write log file](#how-to-write-log-file) 41 - [Custom Log Format](#custom-log-format) 42 - [Controlling Log output coloring](#controlling-log-output-coloring) 43 - [Model binding and validation](#model-binding-and-validation) 44 - [Custom Validators](#custom-validators) 45 - [Only Bind Query String](#only-bind-query-string) 46 - [Bind Query String or Post Data](#bind-query-string-or-post-data) 47 - [Bind Uri](#bind-uri) 48 - [Bind Header](#bind-header) 49 - [Bind HTML checkboxes](#bind-html-checkboxes) 50 - [Multipart/Urlencoded binding](#multiparturlencoded-binding) 51 - [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering) 52 - [SecureJSON](#securejson) 53 - [JSONP](#jsonp) 54 - [AsciiJSON](#asciijson) 55 - [PureJSON](#purejson) 56 - [Serving static files](#serving-static-files) 57 - [Serving data from file](#serving-data-from-file) 58 - [Serving data from reader](#serving-data-from-reader) 59 - [HTML rendering](#html-rendering) 60 - [Custom Template renderer](#custom-template-renderer) 61 - [Custom Delimiters](#custom-delimiters) 62 - [Custom Template Funcs](#custom-template-funcs) 63 - [Multitemplate](#multitemplate) 64 - [Redirects](#redirects) 65 - [Custom Middleware](#custom-middleware) 66 - [Using BasicAuth() middleware](#using-basicauth-middleware) 67 - [Goroutines inside a middleware](#goroutines-inside-a-middleware) 68 - [Custom HTTP configuration](#custom-http-configuration) 69 - [Support Let's Encrypt](#support-lets-encrypt) 70 - [Run multiple service using Gin](#run-multiple-service-using-gin) 71 - [Graceful shutdown or restart](#graceful-shutdown-or-restart) 72 - [Third-party packages](#third-party-packages) 73 - [Manually](#manually) 74 - [Build a single binary with templates](#build-a-single-binary-with-templates) 75 - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct) 76 - [Try to bind body into different structs](#try-to-bind-body-into-different-structs) 77 - [http2 server push](#http2-server-push) 78 - [Define format for the log of routes](#define-format-for-the-log-of-routes) 79 - [Set and get a cookie](#set-and-get-a-cookie) 80 - [Testing](#testing) 81 - [Users](#users) 82 83## Installation 84 85To install Gin package, you need to install Go and set your Go workspace first. 86 871. The first need [Go](https://golang.org/) installed (**version 1.12+ is required**), then you can use the below Go command to install Gin. 88 89```sh 90$ go get -u github.com/gin-gonic/gin 91``` 92 932. Import it in your code: 94 95```go 96import "github.com/gin-gonic/gin" 97``` 98 993. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`. 100 101```go 102import "net/http" 103``` 104 105## Quick start 106 107```sh 108# assume the following codes in example.go file 109$ cat example.go 110``` 111 112```go 113package main 114 115import "github.com/gin-gonic/gin" 116 117func main() { 118 r := gin.Default() 119 r.GET("/ping", func(c *gin.Context) { 120 c.JSON(200, gin.H{ 121 "message": "pong", 122 }) 123 }) 124 r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") 125} 126``` 127 128``` 129# run example.go and visit 0.0.0.0:8080/ping (for windows "localhost:8080/ping") on browser 130$ go run example.go 131``` 132 133## Benchmarks 134 135Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter) 136 137[See all benchmarks](/BENCHMARKS.md) 138 139| Benchmark name | (1) | (2) | (3) | (4) | 140| ------------------------------ | ---------:| ---------------:| ------------:| ---------------:| 141| BenchmarkGin_GithubAll | **43550** | **27364 ns/op** | **0 B/op** | **0 allocs/op** | 142| BenchmarkAce_GithubAll | 40543 | 29670 ns/op | 0 B/op | 0 allocs/op | 143| BenchmarkAero_GithubAll | 57632 | 20648 ns/op | 0 B/op | 0 allocs/op | 144| BenchmarkBear_GithubAll | 9234 | 216179 ns/op | 86448 B/op | 943 allocs/op | 145| BenchmarkBeego_GithubAll | 7407 | 243496 ns/op | 71456 B/op | 609 allocs/op | 146| BenchmarkBone_GithubAll | 420 | 2922835 ns/op | 720160 B/op | 8620 allocs/op | 147| BenchmarkChi_GithubAll | 7620 | 238331 ns/op | 87696 B/op | 609 allocs/op | 148| BenchmarkDenco_GithubAll | 18355 | 64494 ns/op | 20224 B/op | 167 allocs/op | 149| BenchmarkEcho_GithubAll | 31251 | 38479 ns/op | 0 B/op | 0 allocs/op | 150| BenchmarkGocraftWeb_GithubAll | 4117 | 300062 ns/op | 131656 B/op | 1686 allocs/op | 151| BenchmarkGoji_GithubAll | 3274 | 416158 ns/op | 56112 B/op | 334 allocs/op | 152| BenchmarkGojiv2_GithubAll | 1402 | 870518 ns/op | 352720 B/op | 4321 allocs/op | 153| BenchmarkGoJsonRest_GithubAll | 2976 | 401507 ns/op | 134371 B/op | 2737 allocs/op | 154| BenchmarkGoRestful_GithubAll | 410 | 2913158 ns/op | 910144 B/op | 2938 allocs/op | 155| BenchmarkGorillaMux_GithubAll | 346 | 3384987 ns/op | 251650 B/op | 1994 allocs/op | 156| BenchmarkGowwwRouter_GithubAll | 10000 | 143025 ns/op | 72144 B/op | 501 allocs/op | 157| BenchmarkHttpRouter_GithubAll | 55938 | 21360 ns/op | 0 B/op | 0 allocs/op | 158| BenchmarkHttpTreeMux_GithubAll | 10000 | 153944 ns/op | 65856 B/op | 671 allocs/op | 159| BenchmarkKocha_GithubAll | 10000 | 106315 ns/op | 23304 B/op | 843 allocs/op | 160| BenchmarkLARS_GithubAll | 47779 | 25084 ns/op | 0 B/op | 0 allocs/op | 161| BenchmarkMacaron_GithubAll | 3266 | 371907 ns/op | 149409 B/op | 1624 allocs/op | 162| BenchmarkMartini_GithubAll | 331 | 3444706 ns/op | 226551 B/op | 2325 allocs/op | 163| BenchmarkPat_GithubAll | 273 | 4381818 ns/op | 1483152 B/op | 26963 allocs/op | 164| BenchmarkPossum_GithubAll | 10000 | 164367 ns/op | 84448 B/op | 609 allocs/op | 165| BenchmarkR2router_GithubAll | 10000 | 160220 ns/op | 77328 B/op | 979 allocs/op | 166| BenchmarkRivet_GithubAll | 14625 | 82453 ns/op | 16272 B/op | 167 allocs/op | 167| BenchmarkTango_GithubAll | 6255 | 279611 ns/op | 63826 B/op | 1618 allocs/op | 168| BenchmarkTigerTonic_GithubAll | 2008 | 687874 ns/op | 193856 B/op | 4474 allocs/op | 169| BenchmarkTraffic_GithubAll | 355 | 3478508 ns/op | 820744 B/op | 14114 allocs/op | 170| BenchmarkVulcan_GithubAll | 6885 | 193333 ns/op | 19894 B/op | 609 allocs/op | 171 172- (1): Total Repetitions achieved in constant time, higher means more confident result 173- (2): Single Repetition Duration (ns/op), lower is better 174- (3): Heap Memory (B/op), lower is better 175- (4): Average Allocations per Repetition (allocs/op), lower is better 176 177## Gin v1. stable 178 179- [x] Zero allocation router. 180- [x] Still the fastest http router and framework. From routing to writing. 181- [x] Complete suite of unit tests. 182- [x] Battle tested. 183- [x] API frozen, new releases will not break your code. 184 185## Build with [jsoniter](https://github.com/json-iterator/go) 186 187Gin uses `encoding/json` as default json package but you can change to [jsoniter](https://github.com/json-iterator/go) by build from other tags. 188 189```sh 190$ go build -tags=jsoniter . 191``` 192 193## API Examples 194 195You can find a number of ready-to-run examples at [Gin examples repository](https://github.com/gin-gonic/examples). 196 197### Using GET, POST, PUT, PATCH, DELETE and OPTIONS 198 199```go 200func main() { 201 // Creates a gin router with default middleware: 202 // logger and recovery (crash-free) middleware 203 router := gin.Default() 204 205 router.GET("/someGet", getting) 206 router.POST("/somePost", posting) 207 router.PUT("/somePut", putting) 208 router.DELETE("/someDelete", deleting) 209 router.PATCH("/somePatch", patching) 210 router.HEAD("/someHead", head) 211 router.OPTIONS("/someOptions", options) 212 213 // By default it serves on :8080 unless a 214 // PORT environment variable was defined. 215 router.Run() 216 // router.Run(":3000") for a hard coded port 217} 218``` 219 220### Parameters in path 221 222```go 223func main() { 224 router := gin.Default() 225 226 // This handler will match /user/john but will not match /user/ or /user 227 router.GET("/user/:name", func(c *gin.Context) { 228 name := c.Param("name") 229 c.String(http.StatusOK, "Hello %s", name) 230 }) 231 232 // However, this one will match /user/john/ and also /user/john/send 233 // If no other routers match /user/john, it will redirect to /user/john/ 234 router.GET("/user/:name/*action", func(c *gin.Context) { 235 name := c.Param("name") 236 action := c.Param("action") 237 message := name + " is " + action 238 c.String(http.StatusOK, message) 239 }) 240 241 // For each matched request Context will hold the route definition 242 router.POST("/user/:name/*action", func(c *gin.Context) { 243 c.FullPath() == "/user/:name/*action" // true 244 }) 245 246 // This handler will add a new router for /user/groups. 247 // Exact routes are resolved before param routes, regardless of the order they were defined. 248 // Routes starting with /user/groups are never interpreted as /user/:name/... routes 249 router.GET("/user/groups", func(c *gin.Context) { 250 c.String(http.StatusOK, "The available groups are [...]", name) 251 }) 252 253 router.Run(":8080") 254} 255``` 256 257### Querystring parameters 258 259```go 260func main() { 261 router := gin.Default() 262 263 // Query string parameters are parsed using the existing underlying request object. 264 // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe 265 router.GET("/welcome", func(c *gin.Context) { 266 firstname := c.DefaultQuery("firstname", "Guest") 267 lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") 268 269 c.String(http.StatusOK, "Hello %s %s", firstname, lastname) 270 }) 271 router.Run(":8080") 272} 273``` 274 275### Multipart/Urlencoded Form 276 277```go 278func main() { 279 router := gin.Default() 280 281 router.POST("/form_post", func(c *gin.Context) { 282 message := c.PostForm("message") 283 nick := c.DefaultPostForm("nick", "anonymous") 284 285 c.JSON(200, gin.H{ 286 "status": "posted", 287 "message": message, 288 "nick": nick, 289 }) 290 }) 291 router.Run(":8080") 292} 293``` 294 295### Another example: query + post form 296 297``` 298POST /post?id=1234&page=1 HTTP/1.1 299Content-Type: application/x-www-form-urlencoded 300 301name=manu&message=this_is_great 302``` 303 304```go 305func main() { 306 router := gin.Default() 307 308 router.POST("/post", func(c *gin.Context) { 309 310 id := c.Query("id") 311 page := c.DefaultQuery("page", "0") 312 name := c.PostForm("name") 313 message := c.PostForm("message") 314 315 fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) 316 }) 317 router.Run(":8080") 318} 319``` 320 321``` 322id: 1234; page: 1; name: manu; message: this_is_great 323``` 324 325### Map as querystring or postform parameters 326 327``` 328POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1 329Content-Type: application/x-www-form-urlencoded 330 331names[first]=thinkerou&names[second]=tianou 332``` 333 334```go 335func main() { 336 router := gin.Default() 337 338 router.POST("/post", func(c *gin.Context) { 339 340 ids := c.QueryMap("ids") 341 names := c.PostFormMap("names") 342 343 fmt.Printf("ids: %v; names: %v", ids, names) 344 }) 345 router.Run(":8080") 346} 347``` 348 349``` 350ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou] 351``` 352 353### Upload files 354 355#### Single file 356 357References issue [#774](https://github.com/gin-gonic/gin/issues/774) and detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/single). 358 359`file.Filename` **SHOULD NOT** be trusted. See [`Content-Disposition` on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Directives) and [#1693](https://github.com/gin-gonic/gin/issues/1693) 360 361> The filename is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done. 362 363```go 364func main() { 365 router := gin.Default() 366 // Set a lower memory limit for multipart forms (default is 32 MiB) 367 router.MaxMultipartMemory = 8 << 20 // 8 MiB 368 router.POST("/upload", func(c *gin.Context) { 369 // single file 370 file, _ := c.FormFile("file") 371 log.Println(file.Filename) 372 373 // Upload the file to specific dst. 374 c.SaveUploadedFile(file, dst) 375 376 c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) 377 }) 378 router.Run(":8080") 379} 380``` 381 382How to `curl`: 383 384```bash 385curl -X POST http://localhost:8080/upload \ 386 -F "file=@/Users/appleboy/test.zip" \ 387 -H "Content-Type: multipart/form-data" 388``` 389 390#### Multiple files 391 392See the detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/multiple). 393 394```go 395func main() { 396 router := gin.Default() 397 // Set a lower memory limit for multipart forms (default is 32 MiB) 398 router.MaxMultipartMemory = 8 << 20 // 8 MiB 399 router.POST("/upload", func(c *gin.Context) { 400 // Multipart form 401 form, _ := c.MultipartForm() 402 files := form.File["upload[]"] 403 404 for _, file := range files { 405 log.Println(file.Filename) 406 407 // Upload the file to specific dst. 408 c.SaveUploadedFile(file, dst) 409 } 410 c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) 411 }) 412 router.Run(":8080") 413} 414``` 415 416How to `curl`: 417 418```bash 419curl -X POST http://localhost:8080/upload \ 420 -F "upload[]=@/Users/appleboy/test1.zip" \ 421 -F "upload[]=@/Users/appleboy/test2.zip" \ 422 -H "Content-Type: multipart/form-data" 423``` 424 425### Grouping routes 426 427```go 428func main() { 429 router := gin.Default() 430 431 // Simple group: v1 432 v1 := router.Group("/v1") 433 { 434 v1.POST("/login", loginEndpoint) 435 v1.POST("/submit", submitEndpoint) 436 v1.POST("/read", readEndpoint) 437 } 438 439 // Simple group: v2 440 v2 := router.Group("/v2") 441 { 442 v2.POST("/login", loginEndpoint) 443 v2.POST("/submit", submitEndpoint) 444 v2.POST("/read", readEndpoint) 445 } 446 447 router.Run(":8080") 448} 449``` 450 451### Blank Gin without middleware by default 452 453Use 454 455```go 456r := gin.New() 457``` 458 459instead of 460 461```go 462// Default With the Logger and Recovery middleware already attached 463r := gin.Default() 464``` 465 466 467### Using middleware 468```go 469func main() { 470 // Creates a router without any middleware by default 471 r := gin.New() 472 473 // Global middleware 474 // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. 475 // By default gin.DefaultWriter = os.Stdout 476 r.Use(gin.Logger()) 477 478 // Recovery middleware recovers from any panics and writes a 500 if there was one. 479 r.Use(gin.Recovery()) 480 481 // Per route middleware, you can add as many as you desire. 482 r.GET("/benchmark", MyBenchLogger(), benchEndpoint) 483 484 // Authorization group 485 // authorized := r.Group("/", AuthRequired()) 486 // exactly the same as: 487 authorized := r.Group("/") 488 // per group middleware! in this case we use the custom created 489 // AuthRequired() middleware just in the "authorized" group. 490 authorized.Use(AuthRequired()) 491 { 492 authorized.POST("/login", loginEndpoint) 493 authorized.POST("/submit", submitEndpoint) 494 authorized.POST("/read", readEndpoint) 495 496 // nested group 497 testing := authorized.Group("testing") 498 testing.GET("/analytics", analyticsEndpoint) 499 } 500 501 // Listen and serve on 0.0.0.0:8080 502 r.Run(":8080") 503} 504``` 505 506### Custom Recovery behavior 507```go 508func main() { 509 // Creates a router without any middleware by default 510 r := gin.New() 511 512 // Global middleware 513 // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. 514 // By default gin.DefaultWriter = os.Stdout 515 r.Use(gin.Logger()) 516 517 // Recovery middleware recovers from any panics and writes a 500 if there was one. 518 r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { 519 if err, ok := recovered.(string); ok { 520 c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) 521 } 522 c.AbortWithStatus(http.StatusInternalServerError) 523 })) 524 525 r.GET("/panic", func(c *gin.Context) { 526 // panic with a string -- the custom middleware could save this to a database or report it to the user 527 panic("foo") 528 }) 529 530 r.GET("/", func(c *gin.Context) { 531 c.String(http.StatusOK, "ohai") 532 }) 533 534 // Listen and serve on 0.0.0.0:8080 535 r.Run(":8080") 536} 537``` 538 539### How to write log file 540```go 541func main() { 542 // Disable Console Color, you don't need console color when writing the logs to file. 543 gin.DisableConsoleColor() 544 545 // Logging to a file. 546 f, _ := os.Create("gin.log") 547 gin.DefaultWriter = io.MultiWriter(f) 548 549 // Use the following code if you need to write the logs to file and console at the same time. 550 // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) 551 552 router := gin.Default() 553 router.GET("/ping", func(c *gin.Context) { 554 c.String(200, "pong") 555 }) 556 557 router.Run(":8080") 558} 559``` 560 561### Custom Log Format 562```go 563func main() { 564 router := gin.New() 565 566 // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter 567 // By default gin.DefaultWriter = os.Stdout 568 router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { 569 570 // your custom format 571 return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", 572 param.ClientIP, 573 param.TimeStamp.Format(time.RFC1123), 574 param.Method, 575 param.Path, 576 param.Request.Proto, 577 param.StatusCode, 578 param.Latency, 579 param.Request.UserAgent(), 580 param.ErrorMessage, 581 ) 582 })) 583 router.Use(gin.Recovery()) 584 585 router.GET("/ping", func(c *gin.Context) { 586 c.String(200, "pong") 587 }) 588 589 router.Run(":8080") 590} 591``` 592 593**Sample Output** 594``` 595::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" " 596``` 597 598### Controlling Log output coloring 599 600By default, logs output on console should be colorized depending on the detected TTY. 601 602Never colorize logs: 603 604```go 605func main() { 606 // Disable log's color 607 gin.DisableConsoleColor() 608 609 // Creates a gin router with default middleware: 610 // logger and recovery (crash-free) middleware 611 router := gin.Default() 612 613 router.GET("/ping", func(c *gin.Context) { 614 c.String(200, "pong") 615 }) 616 617 router.Run(":8080") 618} 619``` 620 621Always colorize logs: 622 623```go 624func main() { 625 // Force log's color 626 gin.ForceConsoleColor() 627 628 // Creates a gin router with default middleware: 629 // logger and recovery (crash-free) middleware 630 router := gin.Default() 631 632 router.GET("/ping", func(c *gin.Context) { 633 c.String(200, "pong") 634 }) 635 636 router.Run(":8080") 637} 638``` 639 640### Model binding and validation 641 642To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz). 643 644Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags). 645 646Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`. 647 648Also, Gin provides two sets of methods for binding: 649- **Type** - Must bind 650 - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader` 651 - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method. 652- **Type** - Should bind 653 - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader` 654 - **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately. 655 656When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`. 657 658You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, an error will be returned. 659 660```go 661// Binding from JSON 662type Login struct { 663 User string `form:"user" json:"user" xml:"user" binding:"required"` 664 Password string `form:"password" json:"password" xml:"password" binding:"required"` 665} 666 667func main() { 668 router := gin.Default() 669 670 // Example for binding JSON ({"user": "manu", "password": "123"}) 671 router.POST("/loginJSON", func(c *gin.Context) { 672 var json Login 673 if err := c.ShouldBindJSON(&json); err != nil { 674 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 675 return 676 } 677 678 if json.User != "manu" || json.Password != "123" { 679 c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) 680 return 681 } 682 683 c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) 684 }) 685 686 // Example for binding XML ( 687 // <?xml version="1.0" encoding="UTF-8"?> 688 // <root> 689 // <user>user</user> 690 // <password>123</password> 691 // </root>) 692 router.POST("/loginXML", func(c *gin.Context) { 693 var xml Login 694 if err := c.ShouldBindXML(&xml); err != nil { 695 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 696 return 697 } 698 699 if xml.User != "manu" || xml.Password != "123" { 700 c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) 701 return 702 } 703 704 c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) 705 }) 706 707 // Example for binding a HTML form (user=manu&password=123) 708 router.POST("/loginForm", func(c *gin.Context) { 709 var form Login 710 // This will infer what binder to use depending on the content-type header. 711 if err := c.ShouldBind(&form); err != nil { 712 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 713 return 714 } 715 716 if form.User != "manu" || form.Password != "123" { 717 c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) 718 return 719 } 720 721 c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) 722 }) 723 724 // Listen and serve on 0.0.0.0:8080 725 router.Run(":8080") 726} 727``` 728 729**Sample request** 730```shell 731$ curl -v -X POST \ 732 http://localhost:8080/loginJSON \ 733 -H 'content-type: application/json' \ 734 -d '{ "user": "manu" }' 735> POST /loginJSON HTTP/1.1 736> Host: localhost:8080 737> User-Agent: curl/7.51.0 738> Accept: */* 739> content-type: application/json 740> Content-Length: 18 741> 742* upload completely sent off: 18 out of 18 bytes 743< HTTP/1.1 400 Bad Request 744< Content-Type: application/json; charset=utf-8 745< Date: Fri, 04 Aug 2017 03:51:31 GMT 746< Content-Length: 100 747< 748{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"} 749``` 750 751**Skip validate** 752 753When running the above example using the above the `curl` command, it returns error. Because the example use `binding:"required"` for `Password`. If use `binding:"-"` for `Password`, then it will not return error when running the above example again. 754 755### Custom Validators 756 757It is also possible to register custom validators. See the [example code](https://github.com/gin-gonic/examples/tree/master/custom-validation/server.go). 758 759```go 760package main 761 762import ( 763 "net/http" 764 "time" 765 766 "github.com/gin-gonic/gin" 767 "github.com/gin-gonic/gin/binding" 768 "github.com/go-playground/validator/v10" 769) 770 771// Booking contains binded and validated data. 772type Booking struct { 773 CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` 774 CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` 775} 776 777var bookableDate validator.Func = func(fl validator.FieldLevel) bool { 778 date, ok := fl.Field().Interface().(time.Time) 779 if ok { 780 today := time.Now() 781 if today.After(date) { 782 return false 783 } 784 } 785 return true 786} 787 788func main() { 789 route := gin.Default() 790 791 if v, ok := binding.Validator.Engine().(*validator.Validate); ok { 792 v.RegisterValidation("bookabledate", bookableDate) 793 } 794 795 route.GET("/bookable", getBookable) 796 route.Run(":8085") 797} 798 799func getBookable(c *gin.Context) { 800 var b Booking 801 if err := c.ShouldBindWith(&b, binding.Query); err == nil { 802 c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) 803 } else { 804 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 805 } 806} 807``` 808 809```console 810$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17" 811{"message":"Booking dates are valid!"} 812 813$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09" 814{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"} 815 816$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10" 817{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}% 818``` 819 820[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registered this way. 821See the [struct-lvl-validation example](https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations) to learn more. 822 823### Only Bind Query String 824 825`ShouldBindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017). 826 827```go 828package main 829 830import ( 831 "log" 832 833 "github.com/gin-gonic/gin" 834) 835 836type Person struct { 837 Name string `form:"name"` 838 Address string `form:"address"` 839} 840 841func main() { 842 route := gin.Default() 843 route.Any("/testing", startPage) 844 route.Run(":8085") 845} 846 847func startPage(c *gin.Context) { 848 var person Person 849 if c.ShouldBindQuery(&person) == nil { 850 log.Println("====== Only Bind By Query String ======") 851 log.Println(person.Name) 852 log.Println(person.Address) 853 } 854 c.String(200, "Success") 855} 856 857``` 858 859### Bind Query String or Post Data 860 861See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292). 862 863```go 864package main 865 866import ( 867 "log" 868 "time" 869 870 "github.com/gin-gonic/gin" 871) 872 873type Person struct { 874 Name string `form:"name"` 875 Address string `form:"address"` 876 Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` 877 CreateTime time.Time `form:"createTime" time_format:"unixNano"` 878 UnixTime time.Time `form:"unixTime" time_format:"unix"` 879} 880 881func main() { 882 route := gin.Default() 883 route.GET("/testing", startPage) 884 route.Run(":8085") 885} 886 887func startPage(c *gin.Context) { 888 var person Person 889 // If `GET`, only `Form` binding engine (`query`) used. 890 // If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`). 891 // See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48 892 if c.ShouldBind(&person) == nil { 893 log.Println(person.Name) 894 log.Println(person.Address) 895 log.Println(person.Birthday) 896 log.Println(person.CreateTime) 897 log.Println(person.UnixTime) 898 } 899 900 c.String(200, "Success") 901} 902``` 903 904Test it with: 905```sh 906$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033" 907``` 908 909### Bind Uri 910 911See the [detail information](https://github.com/gin-gonic/gin/issues/846). 912 913```go 914package main 915 916import "github.com/gin-gonic/gin" 917 918type Person struct { 919 ID string `uri:"id" binding:"required,uuid"` 920 Name string `uri:"name" binding:"required"` 921} 922 923func main() { 924 route := gin.Default() 925 route.GET("/:name/:id", func(c *gin.Context) { 926 var person Person 927 if err := c.ShouldBindUri(&person); err != nil { 928 c.JSON(400, gin.H{"msg": err}) 929 return 930 } 931 c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID}) 932 }) 933 route.Run(":8088") 934} 935``` 936 937Test it with: 938```sh 939$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3 940$ curl -v localhost:8088/thinkerou/not-uuid 941``` 942 943### Bind Header 944 945```go 946package main 947 948import ( 949 "fmt" 950 "github.com/gin-gonic/gin" 951) 952 953type testHeader struct { 954 Rate int `header:"Rate"` 955 Domain string `header:"Domain"` 956} 957 958func main() { 959 r := gin.Default() 960 r.GET("/", func(c *gin.Context) { 961 h := testHeader{} 962 963 if err := c.ShouldBindHeader(&h); err != nil { 964 c.JSON(200, err) 965 } 966 967 fmt.Printf("%#v\n", h) 968 c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain}) 969 }) 970 971 r.Run() 972 973// client 974// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/ 975// output 976// {"Domain":"music","Rate":300} 977} 978``` 979 980### Bind HTML checkboxes 981 982See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092) 983 984main.go 985 986```go 987... 988 989type myForm struct { 990 Colors []string `form:"colors[]"` 991} 992 993... 994 995func formHandler(c *gin.Context) { 996 var fakeForm myForm 997 c.ShouldBind(&fakeForm) 998 c.JSON(200, gin.H{"color": fakeForm.Colors}) 999} 1000 1001... 1002 1003``` 1004 1005form.html 1006 1007```html 1008<form action="/" method="POST"> 1009 <p>Check some colors</p> 1010 <label for="red">Red</label> 1011 <input type="checkbox" name="colors[]" value="red" id="red"> 1012 <label for="green">Green</label> 1013 <input type="checkbox" name="colors[]" value="green" id="green"> 1014 <label for="blue">Blue</label> 1015 <input type="checkbox" name="colors[]" value="blue" id="blue"> 1016 <input type="submit"> 1017</form> 1018``` 1019 1020result: 1021 1022``` 1023{"color":["red","green","blue"]} 1024``` 1025 1026### Multipart/Urlencoded binding 1027 1028```go 1029type ProfileForm struct { 1030 Name string `form:"name" binding:"required"` 1031 Avatar *multipart.FileHeader `form:"avatar" binding:"required"` 1032 1033 // or for multiple files 1034 // Avatars []*multipart.FileHeader `form:"avatar" binding:"required"` 1035} 1036 1037func main() { 1038 router := gin.Default() 1039 router.POST("/profile", func(c *gin.Context) { 1040 // you can bind multipart form with explicit binding declaration: 1041 // c.ShouldBindWith(&form, binding.Form) 1042 // or you can simply use autobinding with ShouldBind method: 1043 var form ProfileForm 1044 // in this case proper binding will be automatically selected 1045 if err := c.ShouldBind(&form); err != nil { 1046 c.String(http.StatusBadRequest, "bad request") 1047 return 1048 } 1049 1050 err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename) 1051 if err != nil { 1052 c.String(http.StatusInternalServerError, "unknown error") 1053 return 1054 } 1055 1056 // db.Save(&form) 1057 1058 c.String(http.StatusOK, "ok") 1059 }) 1060 router.Run(":8080") 1061} 1062``` 1063 1064Test it with: 1065```sh 1066$ curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile 1067``` 1068 1069### XML, JSON, YAML and ProtoBuf rendering 1070 1071```go 1072func main() { 1073 r := gin.Default() 1074 1075 // gin.H is a shortcut for map[string]interface{} 1076 r.GET("/someJSON", func(c *gin.Context) { 1077 c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) 1078 }) 1079 1080 r.GET("/moreJSON", func(c *gin.Context) { 1081 // You also can use a struct 1082 var msg struct { 1083 Name string `json:"user"` 1084 Message string 1085 Number int 1086 } 1087 msg.Name = "Lena" 1088 msg.Message = "hey" 1089 msg.Number = 123 1090 // Note that msg.Name becomes "user" in the JSON 1091 // Will output : {"user": "Lena", "Message": "hey", "Number": 123} 1092 c.JSON(http.StatusOK, msg) 1093 }) 1094 1095 r.GET("/someXML", func(c *gin.Context) { 1096 c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) 1097 }) 1098 1099 r.GET("/someYAML", func(c *gin.Context) { 1100 c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) 1101 }) 1102 1103 r.GET("/someProtoBuf", func(c *gin.Context) { 1104 reps := []int64{int64(1), int64(2)} 1105 label := "test" 1106 // The specific definition of protobuf is written in the testdata/protoexample file. 1107 data := &protoexample.Test{ 1108 Label: &label, 1109 Reps: reps, 1110 } 1111 // Note that data becomes binary data in the response 1112 // Will output protoexample.Test protobuf serialized data 1113 c.ProtoBuf(http.StatusOK, data) 1114 }) 1115 1116 // Listen and serve on 0.0.0.0:8080 1117 r.Run(":8080") 1118} 1119``` 1120 1121#### SecureJSON 1122 1123Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to response body if the given struct is array values. 1124 1125```go 1126func main() { 1127 r := gin.Default() 1128 1129 // You can also use your own secure json prefix 1130 // r.SecureJsonPrefix(")]}',\n") 1131 1132 r.GET("/someJSON", func(c *gin.Context) { 1133 names := []string{"lena", "austin", "foo"} 1134 1135 // Will output : while(1);["lena","austin","foo"] 1136 c.SecureJSON(http.StatusOK, names) 1137 }) 1138 1139 // Listen and serve on 0.0.0.0:8080 1140 r.Run(":8080") 1141} 1142``` 1143#### JSONP 1144 1145Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists. 1146 1147```go 1148func main() { 1149 r := gin.Default() 1150 1151 r.GET("/JSONP", func(c *gin.Context) { 1152 data := gin.H{ 1153 "foo": "bar", 1154 } 1155 1156 //callback is x 1157 // Will output : x({\"foo\":\"bar\"}) 1158 c.JSONP(http.StatusOK, data) 1159 }) 1160 1161 // Listen and serve on 0.0.0.0:8080 1162 r.Run(":8080") 1163 1164 // client 1165 // curl http://127.0.0.1:8080/JSONP?callback=x 1166} 1167``` 1168 1169#### AsciiJSON 1170 1171Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters. 1172 1173```go 1174func main() { 1175 r := gin.Default() 1176 1177 r.GET("/someJSON", func(c *gin.Context) { 1178 data := gin.H{ 1179 "lang": "GO语言", 1180 "tag": "<br>", 1181 } 1182 1183 // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"} 1184 c.AsciiJSON(http.StatusOK, data) 1185 }) 1186 1187 // Listen and serve on 0.0.0.0:8080 1188 r.Run(":8080") 1189} 1190``` 1191 1192#### PureJSON 1193 1194Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead. 1195This feature is unavailable in Go 1.6 and lower. 1196 1197```go 1198func main() { 1199 r := gin.Default() 1200 1201 // Serves unicode entities 1202 r.GET("/json", func(c *gin.Context) { 1203 c.JSON(200, gin.H{ 1204 "html": "<b>Hello, world!</b>", 1205 }) 1206 }) 1207 1208 // Serves literal characters 1209 r.GET("/purejson", func(c *gin.Context) { 1210 c.PureJSON(200, gin.H{ 1211 "html": "<b>Hello, world!</b>", 1212 }) 1213 }) 1214 1215 // listen and serve on 0.0.0.0:8080 1216 r.Run(":8080") 1217} 1218``` 1219 1220### Serving static files 1221 1222```go 1223func main() { 1224 router := gin.Default() 1225 router.Static("/assets", "./assets") 1226 router.StaticFS("/more_static", http.Dir("my_file_system")) 1227 router.StaticFile("/favicon.ico", "./resources/favicon.ico") 1228 1229 // Listen and serve on 0.0.0.0:8080 1230 router.Run(":8080") 1231} 1232``` 1233 1234### Serving data from file 1235 1236```go 1237func main() { 1238 router := gin.Default() 1239 1240 router.GET("/local/file", func(c *gin.Context) { 1241 c.File("local/file.go") 1242 }) 1243 1244 var fs http.FileSystem = // ... 1245 router.GET("/fs/file", func(c *gin.Context) { 1246 c.FileFromFS("fs/file.go", fs) 1247 }) 1248} 1249 1250``` 1251 1252### Serving data from reader 1253 1254```go 1255func main() { 1256 router := gin.Default() 1257 router.GET("/someDataFromReader", func(c *gin.Context) { 1258 response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png") 1259 if err != nil || response.StatusCode != http.StatusOK { 1260 c.Status(http.StatusServiceUnavailable) 1261 return 1262 } 1263 1264 reader := response.Body 1265 defer reader.Close() 1266 contentLength := response.ContentLength 1267 contentType := response.Header.Get("Content-Type") 1268 1269 extraHeaders := map[string]string{ 1270 "Content-Disposition": `attachment; filename="gopher.png"`, 1271 } 1272 1273 c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders) 1274 }) 1275 router.Run(":8080") 1276} 1277``` 1278 1279### HTML rendering 1280 1281Using LoadHTMLGlob() or LoadHTMLFiles() 1282 1283```go 1284func main() { 1285 router := gin.Default() 1286 router.LoadHTMLGlob("templates/*") 1287 //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") 1288 router.GET("/index", func(c *gin.Context) { 1289 c.HTML(http.StatusOK, "index.tmpl", gin.H{ 1290 "title": "Main website", 1291 }) 1292 }) 1293 router.Run(":8080") 1294} 1295``` 1296 1297templates/index.tmpl 1298 1299```html 1300<html> 1301 <h1> 1302 {{ .title }} 1303 </h1> 1304</html> 1305``` 1306 1307Using templates with same name in different directories 1308 1309```go 1310func main() { 1311 router := gin.Default() 1312 router.LoadHTMLGlob("templates/**/*") 1313 router.GET("/posts/index", func(c *gin.Context) { 1314 c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ 1315 "title": "Posts", 1316 }) 1317 }) 1318 router.GET("/users/index", func(c *gin.Context) { 1319 c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ 1320 "title": "Users", 1321 }) 1322 }) 1323 router.Run(":8080") 1324} 1325``` 1326 1327templates/posts/index.tmpl 1328 1329```html 1330{{ define "posts/index.tmpl" }} 1331<html><h1> 1332 {{ .title }} 1333</h1> 1334<p>Using posts/index.tmpl</p> 1335</html> 1336{{ end }} 1337``` 1338 1339templates/users/index.tmpl 1340 1341```html 1342{{ define "users/index.tmpl" }} 1343<html><h1> 1344 {{ .title }} 1345</h1> 1346<p>Using users/index.tmpl</p> 1347</html> 1348{{ end }} 1349``` 1350 1351#### Custom Template renderer 1352 1353You can also use your own html template render 1354 1355```go 1356import "html/template" 1357 1358func main() { 1359 router := gin.Default() 1360 html := template.Must(template.ParseFiles("file1", "file2")) 1361 router.SetHTMLTemplate(html) 1362 router.Run(":8080") 1363} 1364``` 1365 1366#### Custom Delimiters 1367 1368You may use custom delims 1369 1370```go 1371 r := gin.Default() 1372 r.Delims("{[{", "}]}") 1373 r.LoadHTMLGlob("/path/to/templates") 1374``` 1375 1376#### Custom Template Funcs 1377 1378See the detail [example code](https://github.com/gin-gonic/examples/tree/master/template). 1379 1380main.go 1381 1382```go 1383import ( 1384 "fmt" 1385 "html/template" 1386 "net/http" 1387 "time" 1388 1389 "github.com/gin-gonic/gin" 1390) 1391 1392func formatAsDate(t time.Time) string { 1393 year, month, day := t.Date() 1394 return fmt.Sprintf("%d%02d/%02d", year, month, day) 1395} 1396 1397func main() { 1398 router := gin.Default() 1399 router.Delims("{[{", "}]}") 1400 router.SetFuncMap(template.FuncMap{ 1401 "formatAsDate": formatAsDate, 1402 }) 1403 router.LoadHTMLFiles("./testdata/template/raw.tmpl") 1404 1405 router.GET("/raw", func(c *gin.Context) { 1406 c.HTML(http.StatusOK, "raw.tmpl", gin.H{ 1407 "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), 1408 }) 1409 }) 1410 1411 router.Run(":8080") 1412} 1413 1414``` 1415 1416raw.tmpl 1417 1418```html 1419Date: {[{.now | formatAsDate}]} 1420``` 1421 1422Result: 1423``` 1424Date: 2017/07/01 1425``` 1426 1427### Multitemplate 1428 1429Gin allow by default use only one html.Template. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`. 1430 1431### Redirects 1432 1433Issuing a HTTP redirect is easy. Both internal and external locations are supported. 1434 1435```go 1436r.GET("/test", func(c *gin.Context) { 1437 c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") 1438}) 1439``` 1440 1441Issuing a HTTP redirect from POST. Refer to issue: [#444](https://github.com/gin-gonic/gin/issues/444) 1442```go 1443r.POST("/test", func(c *gin.Context) { 1444 c.Redirect(http.StatusFound, "/foo") 1445}) 1446``` 1447 1448Issuing a Router redirect, use `HandleContext` like below. 1449 1450``` go 1451r.GET("/test", func(c *gin.Context) { 1452 c.Request.URL.Path = "/test2" 1453 r.HandleContext(c) 1454}) 1455r.GET("/test2", func(c *gin.Context) { 1456 c.JSON(200, gin.H{"hello": "world"}) 1457}) 1458``` 1459 1460 1461### Custom Middleware 1462 1463```go 1464func Logger() gin.HandlerFunc { 1465 return func(c *gin.Context) { 1466 t := time.Now() 1467 1468 // Set example variable 1469 c.Set("example", "12345") 1470 1471 // before request 1472 1473 c.Next() 1474 1475 // after request 1476 latency := time.Since(t) 1477 log.Print(latency) 1478 1479 // access the status we are sending 1480 status := c.Writer.Status() 1481 log.Println(status) 1482 } 1483} 1484 1485func main() { 1486 r := gin.New() 1487 r.Use(Logger()) 1488 1489 r.GET("/test", func(c *gin.Context) { 1490 example := c.MustGet("example").(string) 1491 1492 // it would print: "12345" 1493 log.Println(example) 1494 }) 1495 1496 // Listen and serve on 0.0.0.0:8080 1497 r.Run(":8080") 1498} 1499``` 1500 1501### Using BasicAuth() middleware 1502 1503```go 1504// simulate some private data 1505var secrets = gin.H{ 1506 "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, 1507 "austin": gin.H{"email": "austin@example.com", "phone": "666"}, 1508 "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"}, 1509} 1510 1511func main() { 1512 r := gin.Default() 1513 1514 // Group using gin.BasicAuth() middleware 1515 // gin.Accounts is a shortcut for map[string]string 1516 authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ 1517 "foo": "bar", 1518 "austin": "1234", 1519 "lena": "hello2", 1520 "manu": "4321", 1521 })) 1522 1523 // /admin/secrets endpoint 1524 // hit "localhost:8080/admin/secrets 1525 authorized.GET("/secrets", func(c *gin.Context) { 1526 // get user, it was set by the BasicAuth middleware 1527 user := c.MustGet(gin.AuthUserKey).(string) 1528 if secret, ok := secrets[user]; ok { 1529 c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) 1530 } else { 1531 c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) 1532 } 1533 }) 1534 1535 // Listen and serve on 0.0.0.0:8080 1536 r.Run(":8080") 1537} 1538``` 1539 1540### Goroutines inside a middleware 1541 1542When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy. 1543 1544```go 1545func main() { 1546 r := gin.Default() 1547 1548 r.GET("/long_async", func(c *gin.Context) { 1549 // create copy to be used inside the goroutine 1550 cCp := c.Copy() 1551 go func() { 1552 // simulate a long task with time.Sleep(). 5 seconds 1553 time.Sleep(5 * time.Second) 1554 1555 // note that you are using the copied context "cCp", IMPORTANT 1556 log.Println("Done! in path " + cCp.Request.URL.Path) 1557 }() 1558 }) 1559 1560 r.GET("/long_sync", func(c *gin.Context) { 1561 // simulate a long task with time.Sleep(). 5 seconds 1562 time.Sleep(5 * time.Second) 1563 1564 // since we are NOT using a goroutine, we do not have to copy the context 1565 log.Println("Done! in path " + c.Request.URL.Path) 1566 }) 1567 1568 // Listen and serve on 0.0.0.0:8080 1569 r.Run(":8080") 1570} 1571``` 1572 1573### Custom HTTP configuration 1574 1575Use `http.ListenAndServe()` directly, like this: 1576 1577```go 1578func main() { 1579 router := gin.Default() 1580 http.ListenAndServe(":8080", router) 1581} 1582``` 1583or 1584 1585```go 1586func main() { 1587 router := gin.Default() 1588 1589 s := &http.Server{ 1590 Addr: ":8080", 1591 Handler: router, 1592 ReadTimeout: 10 * time.Second, 1593 WriteTimeout: 10 * time.Second, 1594 MaxHeaderBytes: 1 << 20, 1595 } 1596 s.ListenAndServe() 1597} 1598``` 1599 1600### Support Let's Encrypt 1601 1602example for 1-line LetsEncrypt HTTPS servers. 1603 1604```go 1605package main 1606 1607import ( 1608 "log" 1609 1610 "github.com/gin-gonic/autotls" 1611 "github.com/gin-gonic/gin" 1612) 1613 1614func main() { 1615 r := gin.Default() 1616 1617 // Ping handler 1618 r.GET("/ping", func(c *gin.Context) { 1619 c.String(200, "pong") 1620 }) 1621 1622 log.Fatal(autotls.Run(r, "example1.com", "example2.com")) 1623} 1624``` 1625 1626example for custom autocert manager. 1627 1628```go 1629package main 1630 1631import ( 1632 "log" 1633 1634 "github.com/gin-gonic/autotls" 1635 "github.com/gin-gonic/gin" 1636 "golang.org/x/crypto/acme/autocert" 1637) 1638 1639func main() { 1640 r := gin.Default() 1641 1642 // Ping handler 1643 r.GET("/ping", func(c *gin.Context) { 1644 c.String(200, "pong") 1645 }) 1646 1647 m := autocert.Manager{ 1648 Prompt: autocert.AcceptTOS, 1649 HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), 1650 Cache: autocert.DirCache("/var/www/.cache"), 1651 } 1652 1653 log.Fatal(autotls.RunWithManager(r, &m)) 1654} 1655``` 1656 1657### Run multiple service using Gin 1658 1659See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example: 1660 1661```go 1662package main 1663 1664import ( 1665 "log" 1666 "net/http" 1667 "time" 1668 1669 "github.com/gin-gonic/gin" 1670 "golang.org/x/sync/errgroup" 1671) 1672 1673var ( 1674 g errgroup.Group 1675) 1676 1677func router01() http.Handler { 1678 e := gin.New() 1679 e.Use(gin.Recovery()) 1680 e.GET("/", func(c *gin.Context) { 1681 c.JSON( 1682 http.StatusOK, 1683 gin.H{ 1684 "code": http.StatusOK, 1685 "error": "Welcome server 01", 1686 }, 1687 ) 1688 }) 1689 1690 return e 1691} 1692 1693func router02() http.Handler { 1694 e := gin.New() 1695 e.Use(gin.Recovery()) 1696 e.GET("/", func(c *gin.Context) { 1697 c.JSON( 1698 http.StatusOK, 1699 gin.H{ 1700 "code": http.StatusOK, 1701 "error": "Welcome server 02", 1702 }, 1703 ) 1704 }) 1705 1706 return e 1707} 1708 1709func main() { 1710 server01 := &http.Server{ 1711 Addr: ":8080", 1712 Handler: router01(), 1713 ReadTimeout: 5 * time.Second, 1714 WriteTimeout: 10 * time.Second, 1715 } 1716 1717 server02 := &http.Server{ 1718 Addr: ":8081", 1719 Handler: router02(), 1720 ReadTimeout: 5 * time.Second, 1721 WriteTimeout: 10 * time.Second, 1722 } 1723 1724 g.Go(func() error { 1725 err := server01.ListenAndServe() 1726 if err != nil && err != http.ErrServerClosed { 1727 log.Fatal(err) 1728 } 1729 return err 1730 }) 1731 1732 g.Go(func() error { 1733 err := server02.ListenAndServe() 1734 if err != nil && err != http.ErrServerClosed { 1735 log.Fatal(err) 1736 } 1737 return err 1738 }) 1739 1740 if err := g.Wait(); err != nil { 1741 log.Fatal(err) 1742 } 1743} 1744``` 1745 1746### Graceful shutdown or restart 1747 1748There are a few approaches you can use to perform a graceful shutdown or restart. You can make use of third-party packages specifically built for that, or you can manually do the same with the functions and methods from the built-in packages. 1749 1750#### Third-party packages 1751 1752We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer to issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details. 1753 1754```go 1755router := gin.Default() 1756router.GET("/", handler) 1757// [...] 1758endless.ListenAndServe(":4242", router) 1759``` 1760 1761Alternatives: 1762 1763* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. 1764* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server. 1765* [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers. 1766 1767#### Manually 1768 1769In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using `http.Server`'s built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin [here](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown). 1770 1771```go 1772// +build go1.8 1773 1774package main 1775 1776import ( 1777 "context" 1778 "log" 1779 "net/http" 1780 "os" 1781 "os/signal" 1782 "syscall" 1783 "time" 1784 1785 "github.com/gin-gonic/gin" 1786) 1787 1788func main() { 1789 router := gin.Default() 1790 router.GET("/", func(c *gin.Context) { 1791 time.Sleep(5 * time.Second) 1792 c.String(http.StatusOK, "Welcome Gin Server") 1793 }) 1794 1795 srv := &http.Server{ 1796 Addr: ":8080", 1797 Handler: router, 1798 } 1799 1800 // Initializing the server in a goroutine so that 1801 // it won't block the graceful shutdown handling below 1802 go func() { 1803 if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { 1804 log.Printf("listen: %s\n", err) 1805 } 1806 }() 1807 1808 // Wait for interrupt signal to gracefully shutdown the server with 1809 // a timeout of 5 seconds. 1810 quit := make(chan os.Signal) 1811 // kill (no param) default send syscall.SIGTERM 1812 // kill -2 is syscall.SIGINT 1813 // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it 1814 signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) 1815 <-quit 1816 log.Println("Shutting down server...") 1817 1818 // The context is used to inform the server it has 5 seconds to finish 1819 // the request it is currently handling 1820 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 1821 defer cancel() 1822 1823 if err := srv.Shutdown(ctx); err != nil { 1824 log.Fatal("Server forced to shutdown:", err) 1825 } 1826 1827 log.Println("Server exiting") 1828} 1829``` 1830 1831### Build a single binary with templates 1832 1833You can build a server into a single binary containing templates by using [go-assets][]. 1834 1835[go-assets]: https://github.com/jessevdk/go-assets 1836 1837```go 1838func main() { 1839 r := gin.New() 1840 1841 t, err := loadTemplate() 1842 if err != nil { 1843 panic(err) 1844 } 1845 r.SetHTMLTemplate(t) 1846 1847 r.GET("/", func(c *gin.Context) { 1848 c.HTML(http.StatusOK, "/html/index.tmpl",nil) 1849 }) 1850 r.Run(":8080") 1851} 1852 1853// loadTemplate loads templates embedded by go-assets-builder 1854func loadTemplate() (*template.Template, error) { 1855 t := template.New("") 1856 for name, file := range Assets.Files { 1857 defer file.Close() 1858 if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { 1859 continue 1860 } 1861 h, err := ioutil.ReadAll(file) 1862 if err != nil { 1863 return nil, err 1864 } 1865 t, err = t.New(name).Parse(string(h)) 1866 if err != nil { 1867 return nil, err 1868 } 1869 } 1870 return t, nil 1871} 1872``` 1873 1874See a complete example in the `https://github.com/gin-gonic/examples/tree/master/assets-in-binary` directory. 1875 1876### Bind form-data request with custom struct 1877 1878The follow example using custom struct: 1879 1880```go 1881type StructA struct { 1882 FieldA string `form:"field_a"` 1883} 1884 1885type StructB struct { 1886 NestedStruct StructA 1887 FieldB string `form:"field_b"` 1888} 1889 1890type StructC struct { 1891 NestedStructPointer *StructA 1892 FieldC string `form:"field_c"` 1893} 1894 1895type StructD struct { 1896 NestedAnonyStruct struct { 1897 FieldX string `form:"field_x"` 1898 } 1899 FieldD string `form:"field_d"` 1900} 1901 1902func GetDataB(c *gin.Context) { 1903 var b StructB 1904 c.Bind(&b) 1905 c.JSON(200, gin.H{ 1906 "a": b.NestedStruct, 1907 "b": b.FieldB, 1908 }) 1909} 1910 1911func GetDataC(c *gin.Context) { 1912 var b StructC 1913 c.Bind(&b) 1914 c.JSON(200, gin.H{ 1915 "a": b.NestedStructPointer, 1916 "c": b.FieldC, 1917 }) 1918} 1919 1920func GetDataD(c *gin.Context) { 1921 var b StructD 1922 c.Bind(&b) 1923 c.JSON(200, gin.H{ 1924 "x": b.NestedAnonyStruct, 1925 "d": b.FieldD, 1926 }) 1927} 1928 1929func main() { 1930 r := gin.Default() 1931 r.GET("/getb", GetDataB) 1932 r.GET("/getc", GetDataC) 1933 r.GET("/getd", GetDataD) 1934 1935 r.Run() 1936} 1937``` 1938 1939Using the command `curl` command result: 1940 1941``` 1942$ curl "http://localhost:8080/getb?field_a=hello&field_b=world" 1943{"a":{"FieldA":"hello"},"b":"world"} 1944$ curl "http://localhost:8080/getc?field_a=hello&field_c=world" 1945{"a":{"FieldA":"hello"},"c":"world"} 1946$ curl "http://localhost:8080/getd?field_x=hello&field_d=world" 1947{"d":"world","x":{"FieldX":"hello"}} 1948``` 1949 1950### Try to bind body into different structs 1951 1952The normal methods for binding request body consumes `c.Request.Body` and they 1953cannot be called multiple times. 1954 1955```go 1956type formA struct { 1957 Foo string `json:"foo" xml:"foo" binding:"required"` 1958} 1959 1960type formB struct { 1961 Bar string `json:"bar" xml:"bar" binding:"required"` 1962} 1963 1964func SomeHandler(c *gin.Context) { 1965 objA := formA{} 1966 objB := formB{} 1967 // This c.ShouldBind consumes c.Request.Body and it cannot be reused. 1968 if errA := c.ShouldBind(&objA); errA == nil { 1969 c.String(http.StatusOK, `the body should be formA`) 1970 // Always an error is occurred by this because c.Request.Body is EOF now. 1971 } else if errB := c.ShouldBind(&objB); errB == nil { 1972 c.String(http.StatusOK, `the body should be formB`) 1973 } else { 1974 ... 1975 } 1976} 1977``` 1978 1979For this, you can use `c.ShouldBindBodyWith`. 1980 1981```go 1982func SomeHandler(c *gin.Context) { 1983 objA := formA{} 1984 objB := formB{} 1985 // This reads c.Request.Body and stores the result into the context. 1986 if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil { 1987 c.String(http.StatusOK, `the body should be formA`) 1988 // At this time, it reuses body stored in the context. 1989 } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil { 1990 c.String(http.StatusOK, `the body should be formB JSON`) 1991 // And it can accepts other formats 1992 } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil { 1993 c.String(http.StatusOK, `the body should be formB XML`) 1994 } else { 1995 ... 1996 } 1997} 1998``` 1999 2000* `c.ShouldBindBodyWith` stores body into the context before binding. This has 2001a slight impact to performance, so you should not use this method if you are 2002enough to call binding at once. 2003* This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`, 2004`ProtoBuf`. For other formats, `Query`, `Form`, `FormPost`, `FormMultipart`, 2005can be called by `c.ShouldBind()` multiple times without any damage to 2006performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)). 2007 2008### http2 server push 2009 2010http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information. 2011 2012```go 2013package main 2014 2015import ( 2016 "html/template" 2017 "log" 2018 2019 "github.com/gin-gonic/gin" 2020) 2021 2022var html = template.Must(template.New("https").Parse(` 2023<html> 2024<head> 2025 <title>Https Test</title> 2026 <script src="/assets/app.js"></script> 2027</head> 2028<body> 2029 <h1 style="color:red;">Welcome, Ginner!</h1> 2030</body> 2031</html> 2032`)) 2033 2034func main() { 2035 r := gin.Default() 2036 r.Static("/assets", "./assets") 2037 r.SetHTMLTemplate(html) 2038 2039 r.GET("/", func(c *gin.Context) { 2040 if pusher := c.Writer.Pusher(); pusher != nil { 2041 // use pusher.Push() to do server push 2042 if err := pusher.Push("/assets/app.js", nil); err != nil { 2043 log.Printf("Failed to push: %v", err) 2044 } 2045 } 2046 c.HTML(200, "https", gin.H{ 2047 "status": "success", 2048 }) 2049 }) 2050 2051 // Listen and Server in https://127.0.0.1:8080 2052 r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key") 2053} 2054``` 2055 2056### Define format for the log of routes 2057 2058The default log of routes is: 2059``` 2060[GIN-debug] POST /foo --> main.main.func1 (3 handlers) 2061[GIN-debug] GET /bar --> main.main.func2 (3 handlers) 2062[GIN-debug] GET /status --> main.main.func3 (3 handlers) 2063``` 2064 2065If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with `gin.DebugPrintRouteFunc`. 2066In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs. 2067```go 2068import ( 2069 "log" 2070 "net/http" 2071 2072 "github.com/gin-gonic/gin" 2073) 2074 2075func main() { 2076 r := gin.Default() 2077 gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { 2078 log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) 2079 } 2080 2081 r.POST("/foo", func(c *gin.Context) { 2082 c.JSON(http.StatusOK, "foo") 2083 }) 2084 2085 r.GET("/bar", func(c *gin.Context) { 2086 c.JSON(http.StatusOK, "bar") 2087 }) 2088 2089 r.GET("/status", func(c *gin.Context) { 2090 c.JSON(http.StatusOK, "ok") 2091 }) 2092 2093 // Listen and Server in http://0.0.0.0:8080 2094 r.Run() 2095} 2096``` 2097 2098### Set and get a cookie 2099 2100```go 2101import ( 2102 "fmt" 2103 2104 "github.com/gin-gonic/gin" 2105) 2106 2107func main() { 2108 2109 router := gin.Default() 2110 2111 router.GET("/cookie", func(c *gin.Context) { 2112 2113 cookie, err := c.Cookie("gin_cookie") 2114 2115 if err != nil { 2116 cookie = "NotSet" 2117 c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true) 2118 } 2119 2120 fmt.Printf("Cookie value: %s \n", cookie) 2121 }) 2122 2123 router.Run() 2124} 2125``` 2126 2127## Don't trust all proxies 2128 2129Gin lets you specify which headers to hold the real client IP (if any), 2130as well as specifying which proxies (or direct clients) you trust to 2131specify one of these headers. 2132 2133The `TrustedProxies` slice on your `gin.Engine` specifes network addresses or 2134network CIDRs from where clients which their request headers related to client 2135IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or 2136IPv6 CIDRs. 2137 2138```go 2139import ( 2140 "fmt" 2141 2142 "github.com/gin-gonic/gin" 2143) 2144 2145func main() { 2146 2147 router := gin.Default() 2148 router.TrustedProxies = []string{"192.168.1.2"} 2149 2150 router.GET("/", func(c *gin.Context) { 2151 // If the client is 192.168.1.2, use the X-Forwarded-For 2152 // header to deduce the original client IP from the trust- 2153 // worthy parts of that header. 2154 // Otherwise, simply return the direct client IP 2155 fmt.Printf("ClientIP: %s\n", c.ClientIP()) 2156 }) 2157 router.Run() 2158} 2159``` 2160 2161## Testing 2162 2163The `net/http/httptest` package is preferable way for HTTP testing. 2164 2165```go 2166package main 2167 2168func setupRouter() *gin.Engine { 2169 r := gin.Default() 2170 r.GET("/ping", func(c *gin.Context) { 2171 c.String(200, "pong") 2172 }) 2173 return r 2174} 2175 2176func main() { 2177 r := setupRouter() 2178 r.Run(":8080") 2179} 2180``` 2181 2182Test for code example above: 2183 2184```go 2185package main 2186 2187import ( 2188 "net/http" 2189 "net/http/httptest" 2190 "testing" 2191 2192 "github.com/stretchr/testify/assert" 2193) 2194 2195func TestPingRoute(t *testing.T) { 2196 router := setupRouter() 2197 2198 w := httptest.NewRecorder() 2199 req, _ := http.NewRequest("GET", "/ping", nil) 2200 router.ServeHTTP(w, req) 2201 2202 assert.Equal(t, 200, w.Code) 2203 assert.Equal(t, "pong", w.Body.String()) 2204} 2205``` 2206 2207## Users 2208 2209Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. 2210 2211* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. 2212* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform. 2213* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow. 2214* [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares. 2215* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. 2216* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. 2217* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. 2218