Gin framework quick start


Reference learning tutorial :


table of Contents


1. Basic introduction

GinIt is a web framework written in Go language. It is a similar martiniAPI framework but with better performance. Due to its use httprouter, the speed has been increased by nearly 40 times. If you need excellent performance, use Gin



2. Installation and use

First, go mod init 包名create a go module as the project environment through commands, and then perform subsequent operations

1. Download and install gin:

go get -u github.com/gin-gonic/gin

2. Introduce gin into the code:

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

3. Write the first gin instance:

package main

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

func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
	// 当客户端通过get方式请求/hello时,会执行后面的匿名函数
	r.GET("/hello", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello Gin",
		})
	})
	// 启动http服务,默认在8080端口
    r.Run()
}

4. Use postman to test

image-20210527234902480


5. Use Restful API example

package main

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

func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
	// 当客户端通过get方式请求/hello时,会执行后面的匿名函数
	r.GET("/hello", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello Gin",
		})
	})

	r.GET("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "GET",
		})
	})

	r.POST("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "POST",
		})
	})

	r.PUT("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "PUT",
		})
	})

	r.DELETE("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "DELETE",
		})
	})
	// 启动http服务,默认在8080端口
	r.Run()
}

Then use postman to test, using different request methods, requesting the same path/book, will return different results

ps: The browser only supports get/post requests by default. If you want to send a put/delete request, you need to do it through ajax. Therefore, postman testing is recommended here



3. Gin rendering

html rendering

1️⃣Template definition

First create a templatesfolder to store template files , and then define a postfolder and a userfolder according to the business in it, and write the corresponding index.htmltemplate files respectively

post/index.htmlThe content of the file is as follows:

{{define "post/index.html"}}
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <title>post/index</title>
        </head>
        <body>
            {{.title}}
        </body>
    </html>
{{end}}

user/index.htmlThe content of the file is as follows:

{{define "user/index.html"}}
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <title>user/index</title>
        </head>
        <body>
            {{.title}}
        </body>
    </html>
{{end}}

2️⃣Template parsing & rendering

Create and main.gowrite http server code and parse and render HTML templates, you can use LoadHTMLGlob()or LoadHTMLFiles()two methods to render HTML templates

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	//创建默认的路由引擎
	r := gin.Default()

	//模板解析
	// r.LoadHTMLFiles("./template/user/index.html", "./template/post/index.html")
	r.LoadHTMLGlob("./template/**/*")

	//以get方式发/post/index请求
	r.GET("/post/index", func(c *gin.Context) {
		//模板渲染
		c.HTML(http.StatusOK, "post/index.html", gin.H{"title": "this is post/index.html"})
	})

	//以get方式发/user/index请求
	r.GET("user/index", func(c *gin.Context) {
		//模板渲染
		c.HTML(http.StatusOK, "user/index.html", gin.H{"title": "this is user/index.html"})
	})

	//启动http服务在9090端口
	r.Run(":9090")
}

3️⃣ postman test

Test /user/indexand /post/indexview the results separately

image-20210531210237356


image-20210531210257240

Custom template function

If we want to pass parameters <a href='https://bareth.blog.csdn.net/'>BaretH的博客</a>, but do not want to be automatically escaped into a string, then we can customize a template function for corresponding processing, pay attention to the addition of custom functions before template parsing

package main

import (
	"html/template"
	"net/http"

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

func main() {
	//创建默认的路由引擎
	r := gin.Default()

	//自定义函数safe
	r.SetFuncMap(template.FuncMap{
		"safe": func(str string) template.HTML {
			return template.HTML(str)
		},
	})

	//模板解析
	r.LoadHTMLFiles("./index.html")

	//以get方式发/post/index请求
	r.GET("/index", func(c *gin.Context) {
		//模板渲染
		c.HTML(http.StatusOK, "index.html", "<a href='https://bareth.blog.csdn.net/'>BaretH的博客</a>")
	})

	//启动http服务在9090端口
	r.Run(":9090")
}

The template function is defined here safe, and then used in the template file

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <title>自定义函数</title>
    </head>
    <body>
        <div>{{ . | safe }}</div>
    </body>
</html>

Final test:

image-20210531213216006

Static file processing

When the static file is referenced in the HTML file we render, we only need to call the gin.Staticmethod before rendering the page in the following way

Create a staticdirectory here , then create a newindex.css

body {
    background-color: burlywood;
}

Then add the gin.Staticmethod, the method has two parameters, the first is the directory referenced in the html file, and the second parameter is the actual static file storage directory

package main

import (
	"html/template"
	"net/http"
	"github.com/gin-gonic/gin"
)

func main() {
	//创建默认的路由引擎
	r := gin.Default()

	//加载静态文件
	r.Static("/xxx", "./statics")

	//自定义函数safe
	r.SetFuncMap(template.FuncMap{
		"safe": func(str string) template.HTML {
			return template.HTML(str)
		},
	})

	//模板解析
	r.LoadHTMLFiles("./index.html")

	//以get方式发/post/index请求
	r.GET("/index", func(c *gin.Context) {
		//模板渲染
		c.HTML(http.StatusOK, "index.html", "<a href='https://bareth.blog.csdn.net/'>BaretH的博客</a>")
	})

	//启动http服务在9090端口
	r.Run(":9090")
}

Then index.htmlreference the css file in the file

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <title>自定义函数</title>
        <link href="/xxx/index.css"></link>
    </head>
    <body>
        <div>{{ . | safe }}</div>
    </body>
</html>

The test can be seen to take effect:

image-20210531214659995

json rendering

There are currently two mainstream we development methods. One is that the browser requests the server, and the server returns the complete html page content; the second is to define some template files through the front-end framework such as vue and react, and the back-end program only needs to return to The front-end json format data, the front-end renders the json data by itself. So how to return json format data in the Gin framework?

In the Go language, json format data can be expressed in two ways: mapand struct, next we simulate json data, and then request access and return

package main

import (
	"net/http"

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

func main() {
	r := gin.Default()
	//模拟json数据
	//方式一:map
	user1 := gin.H{
		"name": "zsr",
		"age":  21,
		"sex":  "男",
	}
	//方式二:struct
	type User struct {
		Name string
		Age  int
		Sex  string
	}
	user2 := User{
		"gcc",
		21,
		"女",
	}
	r.GET("/user1", func(c *gin.Context) {
		c.JSON(http.StatusOK, user1)
	})
	r.GET("/user2", func(c *gin.Context) {
		c.JSON(http.StatusOK, user2)
	})
	r.Run(":9090")
}

Which gin.His essentially a map

// H is a shortcut for map[string]interface{}
type H map[string]interface{}

Test result:

image-20210531223309337


image-20210531235503647


Note that the c.JSONessence here is the serialization of json data. The default is to read the data through the reflection mechanism of the json package in the Go language. In the Go language, the lowercase of the first letter means that it is not exportable, so in the structure The first letter of the field should be capitalized, so that the json package can read the field in the structure. How about you want the first letter of the field in the structure to be lowercase? We can solve it through the structure tag, and do some custom operations for the structure field

For example, the addition of the following Name field json:nameindicates that when using the json package to manipulate the Name field, the name is lowercase

type User struct {
    Name string	`json:"name"`
    Age  int
    Sex  string
}
image-20210531235606118


Four. Get parameters

Get querystring parameters

Querystring refers to the ?parameters carried in the URL, in the form of key-value pairs, for example:localhost:8080?name=zsr&age=21

We can pass c.DefaultQuery(you can specify the default value to return to the default value if it is not obtained), c.Query(return to empty c.GetQueryif it is not obtained ), (there are two return values, one is the obtained value, and the other is the bool type to indicate whether the acquisition was successful). Method to get:

package main

import (
	"net/http"

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

func main() {
	r := gin.Default()
	r.GET("/hello", func(c *gin.Context) {
		name := c.DefaultQuery("name", "用户名")
		age := c.Query("age")
		c.JSON(http.StatusOK, gin.H{
			"name": name,
			"age":  age,
		})
	})
	r.Run(":9090")
}

Then we initiate a request test, and you can see that the request parameters

image-20210601001559328


are returned successfully. If the name and adreess parameters are not passed, the specified default value will be returned.

image-20210601001428750

Get form parameters

When the data requested by the front end is submitted through the form, how to get the data? The following is a small case

The first is the login page login.html, which contains two parameters to be submitted usernameand the form form, which passwordinitiates a /homerequest when submitted

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>login</title>
    </head>
    <body>
        <form action="/home" method="post">
            用户名:<input type="text" name="username"><br>
            密码:<input type="text" name="password"><br>
            <input type="submit" value="登录">
        </form>
    </body>
</html>

Then there is the home page home.html, which receives the display parameters usernameandpassword

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>login</title>
    </head>
    <body>
        <h1>您的用户名为:{{.username}}</h1>
        <h1>您的密码为:{{.password}}</h1>
    </body>
</html>

Finallymain.go

package main

import (
	"net/http"

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

func main() {
	r := gin.Default()
	r.LoadHTMLFiles("./login.html", "./home.html")
    //处理/login的get请求
	r.GET("/login", func(c *gin.Context) {
		c.HTML(http.StatusOK, "login.html", nil)
	})
    //处理/home的post请求
	r.POST("/home", func(c *gin.Context) {
        //获取表单提交的数据
		username := c.PostForm("username")
		password := c.PostForm("password")
		c.HTML(http.StatusOK, "home.html", gin.H{
			"username": username,
			"password": password,
		})
	})
	r.Run(":9090")
}

There are also the following writing methods when obtaining parameters

//包含默认值
username := c.DefaultPostForm("username", "未知用户名")
password := c.DefaultPostForm("password", "未知密码")

//包含是否获取到结果的bool值
username, nameOk := c.GetPostForm("username")
if !nameOk {
    username = "未知用户名"
}
password, pwdOk := c.GetPostForm("password")
if !pwdOk {
    password = "未知密码"
}

Access test: first access localhost:9090/login, fill in the user name and password,

image-20210601004734725


click login, and then you will be redirected tohome.html

image-20210601004822770

Get path parameter

The requested parameters are passed through the URL path, for example /user/zsr/18:; The method of obtaining the parameters in the request URL path is as follows

func main() {
    //Default返回一个默认的路由引擎
    r := gin.Default()
    r.GET("/user/search/:username/:address", func(c *gin.Context) {
        username := c.Param("username")
        address := c.Param("address")
        //输出json结果给调用方
        c.JSON(http.StatusOK, gin.H{
            "message":  "ok",
            "username": username,
            "address":  address,
        })
    })

    r.Run(":8080")
}

Test access: localhost:9090/user/zsr/18

image-20210601074831848

Note: Do not conflict with matching URLs. For example, if the following request processing is added now, an error will be reported because of path conflicts

r.GET("/user/:year/:month", func(c *gin.Context) {
    year := c.Param("year")
    month := c.Param("month")
    c.JSON(200, gin.H{
        "year": year,
        "age":  month,
    })
})

gin parameter binding

The parameters we get for the front-end request mentioned above are all obtained one by one and then saved in a variable. When there are a lot of request parameters, it will be very inconvenient. In order to be able to obtain request-related parameters more conveniently and improve development efficiency, we can Content-Typeidentify based on the request. and automatically request data extraction request type utilizing reflection QueryString, form表单, JSON, XMLand other parameters of the structure. The following sample code demonstrates the .ShouldBind()powerful function, it can automatically extract the data of the type and type based on the request JSON, form表单and QueryStringbind the value to the specified structure object

package main

import (
	"net/http"

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

type UserInfo struct {
	Username string
	Password string
}

func main() {
	r := gin.Default()
	// 绑定JSON参数的示例
	r.POST("/json", func(c *gin.Context) {
		var user UserInfo
		err := c.ShouldBind(&user) //注意:要通过地址传参,否则为值传递,无法修改数据!!
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error,
			})
		} else {
			c.JSON(http.StatusOK, user)
		}
	})
	// 绑定form表单参数示例
	r.POST("/form", func(c *gin.Context) {
		var user UserInfo
		err := c.ShouldBind(&user) //注意:要通过地址传参,否则为值传递,无法修改数据!!
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error,
			})
		} else {
			c.JSON(http.StatusOK, user)
		}
	})
	// 绑定querystring参数示例
	r.GET("/querystring", func(c *gin.Context) {
		var user UserInfo
		err := c.ShouldBind(&user) //注意:要通过地址传参,否则为值传递,无法修改数据!!
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error,
			})
		} else {
			c.JSON(http.StatusOK, user)
		}
	})
	r.Run(":9090")
}

Test:

image-20210601082820103


image-20210601082901985


image-20210601083037648


We can also add tag tags and specify the name when defining the structure

type UserInfo struct {
	Username string `form:"username" json:"username"`
	Password string `form:"password" json:"password"`
}


5. File upload

File upload is to send http request, so is download

Front-end htmlcode

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>user/index</title>
    </head>
    <body>
        <form action="/upload" method="post" enctype="multipart/form-data">
            <input type="file" name="f1">
            <input type="submit" value="upload your file">
        </form>
    </body>
</html>

Single file upload

package main

import (
	"fmt"
	"net/http"
	"path"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.LoadHTMLFiles("./upload.html")
	r.GET("/upload", func(c *gin.Context) {
		c.HTML(http.StatusOK, "upload.html", nil)
	})
	r.POST("/upload", func(c *gin.Context) {
		//获取上传的单个文件
		file, err := c.FormFile("f1")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"message": err.Error(),
			})
			return
		}
		//定义文件保存路径
		// dst := fmt.Sprintf("./%s", file.Filename)
		dst := path.Join("./", file.Filename)
		//将上传的文件保存在服务端本地
		c.SaveUploadedFile(file, dst)
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
		})
	})
	r.Run(":9090")
}

Then run the test, select the file to upload,

image-20210601140722669


you can see that the upload is successful,

image-20210601140744855


and then you can see the uploaded picture in the current project directory

image-20210601140826112

Multiple file upload

The front-end page is modified to:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>user/index</title>
    </head>
    <body>
        <form action="/upload" method="post" enctype="multipart/form-data">
            <input type="file" name="f1" multiple>
            <input type="submit" value="upload your file">
        </form>
    </body>
</html>

The back end is modified to:

package main

import (
	"net/http"
	"path"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	//处理multipart forms提交文件时默认的内存限制是32 MiB,可以通过下面的方式修改
	//router.MaxMultipartMemory = 8 << 20  // 8 MiB
	r.LoadHTMLFiles("./upload.html")
	r.GET("/upload", func(c *gin.Context) {
		c.HTML(http.StatusOK, "upload.html", nil)
	})
	r.POST("/upload", func(c *gin.Context) {
		//获取上传的多个文件
		form, _ := c.MultipartForm()
		files := form.File["f1"]
		for _, file := range files {
			//定义文件保存路径
			dst := path.Join("./", file.Filename)
			//将上传的文件保存在服务端本地
			c.SaveUploadedFile(file, dst)
		}
		c.JSON(http.StatusOK, gin.H{
			"message": "uploaded ok!",
		})
	})
	r.Run(":9090")
}


Six. Redirect

1️⃣ http redirection : use Redirect, both internal and external redirection are supported

r.GET("/test", func(c *gin.Context) {
    c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})

2️⃣Route redirection : useHandleContext

r.GET("/test", func(c *gin.Context) {
    //跳转到到/test2的路由处理函数
    c.Request.URL.Path = "/test2" //修改请求的url
    r.HandleContext(c) //继续后续的处理
})

r.GET("/test2", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"hello": "world"})
})


Seven. Routing and routing groups

1️⃣Ordinary routing

The four basic request methods

//用于获取信息,类似于sql中的select
r.GET("/get", func(c *gin.Context) {...})

//用于向服务端发送数据,类似于sql中的update,用来修改数据的内容,但是不会增加数据的种类等
r.POST("/post", func(c *gin.Context) {...})

//向服务器端发送数据,但是该请求会改变数据的种类等资源,就像sql中的insert一样,会创建新的内容
r.PUT("/put", func(c *gin.Context) {...})

//用来删除某一个资源的,类似于sql的delete操作
r.DELETE("/put", func(c *gin.Context) {...})

Can match all request methodsAny

r.Any("/any", func(c *gin.Context) {...})

Add a handler for the route without a configured handler (it returns a 404 code by default)

r.NoRoute(func(c *gin.Context) {
    c.HTML(http.StatusNotFound, "views/404.html", nil)
})

2️⃣Routing group

We can divide the routes with a common URL prefix into a routing group. For the sake of beauty, it is habitual to use a pair {}of routes to wrap the same group, and {}there is no difference in the function of whether or not to wrap. Routing groups are often used in the case of dividing business logic or dividing API versions
func main() {
    r := gin.Default()
    userGroup := r.Group("/user")
    {
        userGroup.GET("/index", func(c *gin.Context) {...})
        userGroup.GET("/login", func(c *gin.Context) {...})
        userGroup.POST("/login", func(c *gin.Context) {...})

    }
    shopGroup := r.Group("/shop")
    {
        shopGroup.GET("/index", func(c *gin.Context) {...})
        shopGroup.GET("/cart", func(c *gin.Context) {...})
        shopGroup.POST("/checkout", func(c *gin.Context) {...})
    }
    r.Run()
}

The group by group also supports nesting, for example:

shopGroup := r.Group("/shop")
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    shopGroup.GET("/cart", func(c *gin.Context) {...})
    shopGroup.POST("/checkout", func(c *gin.Context) {...})
    // 嵌套路由组
    xx := shopGroup.Group("xx")
    xx.GET("/oo", func(c *gin.Context) {...})
}


8. Gin middleware

The Gin framework allows developers to add their own hook functions in the process of processing requests. This hook function is called middleware middleware, which is suitable for processing some common business logic, such as login authentication, permission verification, data paging, logging, time-consuming statistics, etc.
image-20210601194235097

Definition of middleware

The middleware in the Gin framework must be a HandlerFunctype, that is, a func(*Context)type, which is a function type and must have *Contextparameters

// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)

Next, customize a middleware Mymiddlewareto count the time-consuming request processing

func Mymiddleware(c *gin.Context) {
	fmt.Println("进入Mymiddleware")
	start := time.Now() //计时
	c.Next() //调用该请求剩余的处理程序
	cost := time.Since(start)
	fmt.Printf("路由处理耗时:%v\n", cost)
	fmt.Println("退出Mymiddleware")
}

note:

  • c.Next(): Indicates that the remaining handlers of the request are called
  • c.Abort(): Indicates that the remaining handlers are prevented from being called

Register middleware

1️⃣Method 1: Register for a single route

When we c.Getwrite routing handlers by other means, the second parameter is HandlerFuncthe variable parameter of the type, so the custom middleware can be passed into it as a parameter

// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodGet, relativePath, handlers)
}

Next we write a routing handler to Mymiddlewareregister the middleware defined above

func main() {
	r := gin.Default()
	r.GET("/index", Mymiddleware, func(c *gin.Context) {
		fmt.Println("执行/index处理函数")
		c.JSON(http.StatusOK, gin.H{
			"message": "ok",
		})
	})
	r.Run(":9090")
}

Then run the test, visit localhost:9090/index , look at the background printing information, you can see that the middleware is passed and then the routing processing function is executed

image-20210601202558019

2️⃣Register for global routing

If we have multiple routing processing functions that want to use the same middleware, it is a bit troublesome to pass parameters each time, we can register through the global routing, one registration, multiple valid

func main() {
	r := gin.Default()
	//全局注册中间件
	r.Use(Mymiddleware)
	r.GET("/index", func(c *gin.Context) {
		fmt.Println("执行/index处理函数")
		c.JSON(http.StatusOK, gin.H{
			"message": "ok",
		})
	})
	r.GET("/index2", func(c *gin.Context) {
		fmt.Println("指定/index2处理函数")
		c.JSON(http.StatusOK, gin.H{
			"message": "ok",
		})
	})
	r.Run(":9090")
}

Run the test, request /indexand /index2, according to the printed information, you can see that the middleware are all effective.

image-20210601202636263


If you define another middleware Mymiddleware2, then register the two middleware globally

func Mymiddleware2(c *gin.Context) {
	fmt.Println("进入Mymiddleware2")
	c.Next() //调用该请求剩余的处理程序
	fmt.Println("退出Mymiddleware2")
}
func main() {
	r := gin.Default()
	//全局注册中间件
	r.Use(Mymiddleware,Mymiddleware2)
	r.GET("/index", func(c *gin.Context) {
		fmt.Println("执行/index处理函数")
		c.JSON(http.StatusOK, gin.H{
			"message": "ok",
		})
	})
}

Re-test, visit /index, and print the results as follows

image-20210601203319334


. The execution sequence of the corresponding programs is as follows:

image-20210601203549006

3️⃣Register middleware for routing group

method one:

xxGroup := r.Group("/shop", Mymiddleware)
{
    xxGroup.GET("/index", func(c *gin.Context) {
        c.JSON(http.StatusOK,gin.H{
            "message":"xx",
        })
    })
}

Way two:

xxGroup := r.Group("/shop")
xxGroup.Use(Mymiddleware)
{
    xxGroup.GET("/index", func(c *gin.Context) {
        c.JSON(http.StatusOK,gin.H{
            "message":"xx",
        })
    })
}

Stored value in middleware

We can store the value in the middleware and get it in the route processing function

Mymiddlerware2By c.Setstoring the name value in the middleware

func Mymiddleware2(c *gin.Context) {
	fmt.Println("进入Mymiddleware2")
	c.Set("name", "zsr")
	c.Next() //调用该请求剩余的处理程序
	fmt.Println("退出Mymiddleware2")
}

In the routing processing function, the name value can be retrieved through the c.Get/ c.MustGetetc function

r.GET("/getValue", func(c *gin.Context) {
    name, ok := c.Get("name")
    if !ok {
        name = "匿名用户"
    } else {
        c.JSON(http.StatusOK, gin.H{
            "name": name,
        })
    }
})

Test Results:

image-20210601210929712

expand

1️⃣ gin default middleware

gin.Default()LoggerAnd Recoverymiddleware are used by default

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

among them:

  • LoggerThe middleware writes the log gin.DefaultWriter, even if it is configuredGIN_MODE=release
  • RecoveryThe middleware will recover anything panic. If there is panic, a 500 response code will be written

If you don’t want to use the above two default middleware, you can gin.New()create a new route without any default middleware

2️⃣ Use goroutine in gin middleware

When handlerstarting a new one in the middleware or in goroutine, the original context (c *gin.Context) cannot be used, and its read-only copy must be used ( c.Copy())

3️⃣Universal middleware template

Taking user authentication as an example, we usually write the middleware in the form of a closure, and then we can add some specific operations to it

func authMiddleware() gin.HandlerFunc{
    //连接数据库或其他一些准备工作
    return func (c *gin.Context)  {
        //存放具体的逻辑,例如进行是否登录的判断
        if 是登录用户{ //如果是用户则进行登录的路由处理
            c.Next()
        }
        else{ //如果不是用户则不处理
            c.Abort()
        }	
    }
}