Hello, everyone, I'm Asong. I have been studying Gin framework recently. In the process of learning, I have been looking at English documents, which is very painful for me because of my poor English. I just want to translate them into Chinese documents for him, which can improve our learning efficiency. There are a lot of translated documents on the Internet, but they are very old, many updates are not processed, not very complete. So I translated this Chinese document while learning English by myself, and now I share it with you. I hope it will be useful to you. Note: As the document is translated by myself, welcome to point out any mistakes. Documents uploaded personal github:https://github.com/sunsong2020/Golang_Dream/tree/master/Gin/Doc version without the watermark for: focus on the public number: Golang dreamworks, background reply Gin, available.Copy the code

Note: only some important documents are posted here. Please click the above method to obtain the full PDF version.

2020Gin Framework Chinese document

@[toc]

The installation

Before installing the Gin package, you need to install the Go environment on your computer and set up your workspace.

  1. First you need to install Go(version 1.11+ support) and then install Gin using the following Go command:
$ go get -u github.com/gin-gonic/gin
Copy the code
  1. Import Gin packages in your code:
import "github.com/gin-gonic/gin"
Copy the code
  1. (Optional) If an example is usedhttp.StatusOKConstants like, need to be introducednet/httpPackage:
import "net/http"
Copy the code

Quick start

# suppose example.go contains the following code: $cat example.goCopy the code
package main

import "github.com/gin-gonic/gin"

func main(a) {
	r := gin.Default()
	r.GET("/ping".func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
Copy the code

API example

You can find many ready-made examples in the repository of Gin examples.

Use GET, POST, PUT, PATCH, DELETE and OPTIONS

func main(a) {
	// Create gin routes using the default middleware (Logger and recovery middleware)
	router := gin.Default()

	router.GET("/someGet", getting)
	router.POST("/somePost", posting)
	router.PUT("/somePut", putting)
	router.DELETE("/someDelete", deleting)
	router.PATCH("/somePatch", patching)
	router.HEAD("/someHead", head)
	router.OPTIONS("/someOptions", options)

	// The service starts on PORT 8080 by default, unless a PORT environment variable is defined. .
	router.Run()
	Router. Run(":3000") Hardcode port number
}
Copy the code

Routing parameters

func main(a) {
	router := gin.Default()

	// This handler will match /user/ John but not /user/ or /user
	router.GET("/user/:name".func(c *gin.Context) {
		name := c.Param("name")
		c.String(http.StatusOK, "Hello %s", name)
	})

	// However, this will match /user/ John/and /user/ John /send
	// If no other router matches /user/ John, it will redirect to /user/ John /
	router.GET("/user/:name/*action".func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		message := name + " is " + action
		c.String(http.StatusOK, message)
	})

	// For each matched request, the context retains the route definition
	router.POST("/user/:name/*action".func(c *gin.Context) {
		c.FullPath() == "/user/:name/*action" // true
	})

	router.Run(": 8080")}Copy the code

Query string parameters

func main(a) {
	router := gin.Default()

	// Query string parameters are parsed using the existing underlying Request object
	// Request response matching URL: /welcome? firstname=Jane&lastname=Doe
	router.GET("/welcome".func(c *gin.Context) {
		firstname := c.DefaultQuery("firstname"."Guest")
		lastname := c.Query("lastname") // this is a shortcut to c.equest.url.query ().get ("lastname")

		c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
	})
	router.Run(": 8080")}Copy the code

Multipart/Urlencoded form

func main(a) {
	router := gin.Default()

	router.POST("/form_post".func(c *gin.Context) {
		message := c.PostForm("message")
		nick := c.DefaultPostForm("nick"."anonymous")

		c.JSON(200, gin.H{
			"status":  "posted"."message": message,
			"nick":    nick,
		})
	})
	router.Run(": 8080")}Copy the code

Other examples: query+ POST forms

POST /post? id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=manu&message=this_is_great

Copy the code
func main(a) {
	router := gin.Default()

	router.POST("/post".func(c *gin.Context) {

		id := c.Query("id")
		page := c.DefaultQuery("page"."0")
		name := c.PostForm("name")
		message := c.PostForm("message")

		fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
	})
	router.Run(": 8080")}Copy the code

Running results:

id: 1234; page: 1; name: manu; message: this_is_great
Copy the code

Map as a query string or post form parameter

POST /post? ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded

names[first]=thinkerou&names[second]=tianou
Copy the code
func main(a) {
	router := gin.Default()

	router.POST("/post".func(c *gin.Context) {

		ids := c.QueryMap("ids")
		names := c.PostFormMap("names")

		fmt.Printf("ids: %v; names: %v", ids, names)
	})
	router.Run(": 8080")}Copy the code

Running results:

ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou]
Copy the code

Upload a file

A single file

Refer to Issue #774 for detailed example code: Example code.

Use file.Filename with caution, as in Content-Disposition on MDN and #1693

The file name of the uploaded file can be customized by the user, so it may contain illegal strings. For security, the server should unify the file name rules.

func main(a) {
	router := gin.Default()
	// Limit the upload size of the form (default: 32 MiB)
	router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload".func(c *gin.Context) {
		// single file
		file, _ := c.FormFile("file")
		log.Println(file.Filename)

		// Upload the file to the specified path
		c.SaveUploadedFile(file, dst)

		c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
	})
	router.Run(": 8080")}Copy the code

The curl test:

curl -X POST http://localhost:8080/upload \
  -F "file=@/Users/appleboy/test.zip" \
  -H "Content-Type: multipart/form-data"
Copy the code

Multiple files

For details, see example code.

func main(a) {
	router := gin.Default()
	// Limit the upload size of the form (default is 32 MiB)
	router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload".func(c *gin.Context) {
		/ / files
		form, _ := c.MultipartForm()
		files := form.File["upload[]"]

		for _, file := range files {
			log.Println(file.Filename)

			// Upload the file to the specified path
			c.SaveUploadedFile(file, dst)
		}
		c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!".len(files)))
	})
	router.Run(": 8080")}Copy the code

The curl test:

curl -X POST http://localhost:8080/upload \
  -F "upload[]=@/Users/appleboy/test1.zip" \
  -F "upload[]=@/Users/appleboy/test2.zip" \
  -H "Content-Type: multipart/form-data"
Copy the code

Routing group

func main(a) {
	router := gin.Default()

	// Simple group: v1
	v1 := router.Group("/v1")
	{
		v1.POST("/login", loginEndpoint)
		v1.POST("/submit", submitEndpoint)
		v1.POST("/read", readEndpoint)
	}

	// Simple group: v2
	v2 := router.Group("/v2")
	{
		v2.POST("/login", loginEndpoint)
		v2.POST("/submit", submitEndpoint)
		v2.POST("/read", readEndpoint)
	}

	router.Run(": 8080")}Copy the code

The default is blank Gin without middleware

Use:

r := gin.New()
Copy the code

Instead of

Logger and Recovery middleware are already connected by default
r := gin.Default()
Copy the code

Using middleware

func main(a) {
	// Create a default route without any middleware
	r := gin.New()

	// Global middleware
	// Logger middleware will write logs to gin.DefaultWriter even if you set GIN_MODE=release.
	// The default setting is gin.DefaultWriter = os.stdout
	r.Use(gin.Logger())

	// Recovery middleware recovers from any panic and writes a 500 error if panic occurs.
	r.Use(gin.Recovery())

	// For each routing middleware, you can add as many as you need
	r.GET("/benchmark", MyBenchLogger(), benchEndpoint)

	/ / authorization group
	// authorized := r.Group("/", AuthRequired())
	// You can do that
	authorized := r.Group("/")
	// Middleware for each group! In this case, we just need to be in the "Authorized" group
	// Use the custom AuthRequired() middleware
	authorized.Use(AuthRequired())
	{
		authorized.POST("/login", loginEndpoint)
		authorized.POST("/submit", submitEndpoint)
		authorized.POST("/read", readEndpoint)

		/ / nested groups
		testing := authorized.Group("testing")
		testing.GET("/analytics", analyticsEndpoint)
	}

	// Listen on and serve 0.0.0.0:8080
	r.Run(": 8080")}Copy the code

How do I write to a log file

func main(a) {
    // Disable console color, you do not need console color when writing logs to files
    gin.DisableConsoleColor()

    // Write to the log file
    f, _ := os.Create("gin.log")
    gin.DefaultWriter = io.MultiWriter(f)

    // If you need to write log files and console display at the same time, use the following code
    // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

    router := gin.Default()
    router.GET("/ping".func(c *gin.Context) {
        c.String(200."pong")
    })

    router.Run(": 8080")}Copy the code

Custom log format

func main(a) {
	router := gin.New()

	// The LoggerWithFormatter middleware will write the log to gin.DefaultWriter
	// Default gin.DefaultWriter = os.stdout
	router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {

		// Your custom format
		return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
				param.ClientIP,
				param.TimeStamp.Format(time.RFC1123),
				param.Method,
				param.Path,
				param.Request.Proto,
				param.StatusCode,
				param.Latency,
				param.Request.UserAgent(),
				param.ErrorMessage,
		)
	}))
	router.Use(gin.Recovery())

	router.GET("/ping".func(c *gin.Context) {
		c.String(200."pong")
	})

	router.Run(": 8080")}Copy the code

Sample output:

: :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.03578.80. Safari/537.36""
Copy the code

Controlling Log Output coloring

By default, the log output from the console should be colored based on the TTY detected.

No log coloring:

func main(a) {
    // Disable the log color
    gin.DisableConsoleColor()
    
    // Create a gin route using the default middleware:
    Logger and recovery (crash-free) middleware
    router := gin.Default()
    
    router.GET("/ping".func(c *gin.Context) {
        c.String(200."pong")
    })
    
    router.Run(": 8080")}Copy the code

Color the log:

func main(a) {
    // Record the log color
    gin.ForceConsoleColor()
    
    // Create a gin route using the default middleware:
    Logger and recovery (crash-free) middleware
    router := gin.Default()
    
    router.GET("/ping".func(c *gin.Context) {
        c.String(200."pong")
    })
    
    router.Run(": 8080")}Copy the code

Model binding and validation

To bind a request body to a structure, use model bindings, which currently support JSON, XML, YAML, and standard form values (foo= bar&Boo =baz).

Gin validates the parameters with Go-playground /validator.v8. Click here to view the full document here

You need to set the tag on the bound field. For example, if the binding format is JSON, you need to set it to JSON :” fieldName”

In addition, Gin provides two binding methods:

  • Type – Must bind

    • Methods – Bind, BindJSON, BindXML, BindQuery, BindYAML, BindHeader
    • Behavior – These methods are used at the bottomMustBindWithIf there is a binding error, the request will be aborted by the following commandc.AbortWithError(400, err).SetType(ErrorTypeBind), the response status code will be set to 400, the request headerContent-TypeIs set totext/plain; charset=utf-8. Note that if you try to set up the response code after this, a warning will be issued[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422If you want better control over behavior, use shouldbind-related methods.
  • Type – Should bind

    • Methods – ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML, ShouldBindHeader.
    • Behavior – These methods are used at the bottomShouldBindWithIf there is a binding error, an error is returned and the developer can handle the request and error correctly. When we use the binding method, Gin will use theContent-TypeFigure out which binder to use, if you’re sure what you’re binding to, you can useMustBindWithorBindingWith.

You can also specify a rule specific modifier for a field. If a field is decorated with binding:”required” and its value is null at binding time, an error will be returned.

// Bind to JSON
type Login struct {
	User     string `form:"user" json:"user" xml:"user" binding:"required"`
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

func main(a) {
	router := gin.Default()

	/ / JSON binding example ({" user ":" manu ", "password" : "123"})
	router.POST("/loginJSON".func(c *gin.Context) {
		var json Login
		iferr := c.ShouldBindJSON(&json); err ! =nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		
		ifjson.User ! ="manu"|| json.Password ! ="123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		} 
		
		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})})// XML binding example (
	/ / 
      
	//	<root>
	// 
      
       user
      
	// 
      
       123
      
	//	</root>)
	router.POST("/loginXML".func(c *gin.Context) {
		var xml Login
		iferr := c.ShouldBindXML(&xml); err ! =nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		
		ifxml.User ! ="manu"|| xml.Password ! ="123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		} 
		
		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})})// Example of binding HTML form (user=manu&password=123)
	router.POST("/loginForm".func(c *gin.Context) {
		var form Login
		// This will use the Content-Type header to infer which dependency the binder will use.
		iferr := c.ShouldBind(&form); err ! =nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		
		ifform.User ! ="manu"|| form.Password ! ="123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		} 
		
		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})})// Listen on and serve 0.0.0.0:8080
	router.Run(": 8080")}Copy the code

Example request:

$ curl -v -X POST \
  http://localhost:8080/loginJSON \
  -H 'content-type: application/json' \
  -d '{ "user": "manu" }'
> POST /loginJSON HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51. 0
> Accept: */*
> content-type: application/json
> Content-Length: 18
>
* upload completely sent off: 18 out of 18 bytes
< HTTP/1.1 400 Bad Request
< Content-Type: application/json; charset=utf-8
< Date: Fri, 04 Aug 2017 03:51:31 GMT
< Content-Length: 100
<
{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}
Copy the code

Skip validation:

Using the curl command above, we return an error because the Password field uses binding:”required”. If we use binding:”-“, it will not return an error. # Run example.go and access 0.0.0.0:8080/ping (Windows access: localhost:8080/ping) $go run example.go

Public account: Golang Dream Factory

Asong is a Golang development engineer, focusing on Golang related technologies: Golang interview, Beego, Gin, Mysql, Linux, Network, operating system, etc., dedicated to Golang development. Welcome to the public account: Golang Dream Factory. Learn together and make progress together.

To obtain the latest Gin Chinese documents, you can directly reply to the background of the official account: Gin. Author Asong regularly maintains.

At the same time, upload personal documents to github: github.com/sunsong2020…