Gin框架快速入门


参考学习教程

  • 中文文档
  • 【最新Go Web开发教程】基于gin框架和gorm的web开发实战 (七米出品)_哔哩哔哩_bilibili

目录

  • 一. 基本介绍
  • 二. 安装与使用
  • 三. Gin渲染
    • html渲染
    • 自定义模板函数
    • 静态文件处理
    • json渲染
  • 四. 获取参数
    • 获取querystring参数
    • 获取form表单参数
    • 获取path参数
    • gin参数绑定
  • 五. 文件上传
    • 单个文件上传
    • 多个文件上传
  • 六. 重定向
  • 七. 路由和路由组
  • 八. Gin中间件
    • 中间件的定义
    • 注册中间件
    • 中间件中存值
    • 拓展


一. 基本介绍

Gin是一个用Go语言编写的web框架。它是一个类似于martini但拥有更好性能的API框架, 由于使用了httprouter,速度提高了近40倍。如果你需要极好的性能,使用 Gin 吧

  • Github地址
  • 中文文档
  • Gin框架介绍及使用 | 李文周的博客 (liwenzhou.com)


二. 安装与使用

首先通过go mod init 包名命令创建一个go模块作为项目环境,然后进行后序操作

1、下载并安装 gin:

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

2、将 gin 引入到代码中:

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

3、编写第一个gin实例:

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、利用postman进行测试
Gin框架快速入门_第1张图片
5、使用Restful API示例

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()
}

然后利用postman进行测试,采用不同的请求方式,请求同样的路径/book,会返回不同结果

ps:浏览器默认只支持get/post请求,如果想要发put/delete请求,需要通过ajax来进行。因此这里推荐使用postman测试



三. Gin渲染

html渲染

1️⃣ 模板定义

首先创建一个存放模板文件的templates文件夹,然后在其内部按照业务分别定义一个post文件夹和一个user文件夹,分别编写对应的index.html模板文件

post/index.html文件的内容如下:

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

user/index.html文件的内容如下:

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

2️⃣ 模板解析&渲染

创建main.go编写http server代码并进行html模板解析渲染,可以使用LoadHTMLGlob()或者LoadHTMLFiles()两种方法进行HTML模板渲染

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测试

分别测试/user/index/post/index查看结果
Gin框架快速入门_第2张图片
Gin框架快速入门_第3张图片


自定义模板函数

如果我们想传参BaretH的博客,但是不希望自动转义成字符串,此时就可以自定义一个模板函数进行相应的处理,注意自定义函数的添加要在模板解析之前

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", "BaretH的博客")
	})

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

这里定义了模板函数safe,然后在模板文件中进行使用

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

最后测试:
Gin框架快速入门_第4张图片


静态文件处理

当我们渲染的HTML文件中引用了静态文件时,我们只需要按照以下方式在渲染页面前调用gin.Static方法即可

这里创建static目录,然后新建index.css

body {
     
    background-color: burlywood;
}

然后添加gin.Static方法,该方法有两个参数,第一个为在html文件中引用的目录,第二个参数为实际的静态文件存放目录

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", "BaretH的博客")
	})

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

然后在index.html文件中引用css文件

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

测试可以看到生效:
Gin框架快速入门_第5张图片


json渲染

目前主流的we开发方式有两种,一种是浏览器请求服务器,服务器返回完整的html页面内容;第二种就是通过前端框架如vue、react定义好一些模板文件,后端程序只需要返回给前端json格式数据,前端拿到json数据后自行渲染。那么在Gin框架中如何返回json格式数据呢?

在Go语言中,json格式数据可以以两种方式表示:mapstruct,接下来我们模拟json数据,然后请求访问并返回

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")
}

其中gin.H本质上就是一个map

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

测试结果:
Gin框架快速入门_第6张图片
Gin框架快速入门_第7张图片
注意这里的c.JSON本质上就是json数据的序列化,默认就是通过Go语言中json包使用反射机制读取数据的,而在Go语言中,首字母小写表示是不可导出的,因此结构体中字段首字母要大写,让json包读取到结构体中的字段,那如过就想要结构体中字段的首字母小写呢?我们可以通过结构体tag来解决,给结构体字段做一些自定制操作

例如下述Name字段后加的json:name表示当使用json包操作Name字段时,名字为小写的name

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

Gin框架快速入门_第8张图片



四. 获取参数

获取querystring参数

querystring指的是URL中?后面携带的参数,是键值对的形式,例如:localhost:8080?name=zsr&age=21

我们可以通过c.DefaultQuery(可指定默认值获取不到就返回默认值)、c.Query(获取不到返回空)、c.GetQuery(有两个返回值,一个是获取的值,一个是bool类型的表示是否获取成功)三种方法来获取:

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")
}

然后我们发起一个请求测试,可以看到成功返回请求参数
Gin框架快速入门_第9张图片
如果不传name和adreess参数,则返回指定的默认值
Gin框架快速入门_第10张图片


获取form表单参数

当前端请求的数据通过form表单提交时,如何获取数据呢?以下是一个小案例

首先是登录页login.html,其中包含form表单有两个待提交参数usernamepassword,提交时发起/home请求

DOCTYPE html>
<html lang="en">
    <head>
        <title>logintitle>
    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>

然后是主页home.html,接收显示参数usernamepassword

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

最后是main.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")
}

其中获取参数时同样还有以下写法

//包含默认值
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 = "未知密码"
}

访问测试:首先访问localhost:9090/login,填写用户名和密码
Gin框架快速入门_第11张图片
点击登录,然后就会跳转到home.html
Gin框架快速入门_第12张图片


获取path参数

请求的参数通过URL路径传递,例如:/user/zsr/18; 获取请求URL路径中的参数的方式如下

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")
}

测试访问:localhost:9090/user/zsr/18
image-20210601074831848

注意:匹配的url不要冲突,比如现在新增以下请求处理,则会报错,因为路径冲突

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参数绑定

上述我们获取前端请求的参数都是一个一个获取然后保存到变量中,当请求参数非常多时,就会十分不方便,为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryStringform表单JSONXML等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提取JSONform表单QueryString类型的数据,并把值绑定到指定的结构体对象

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")
}

测试:
Gin框架快速入门_第13张图片
Gin框架快速入门_第14张图片
Gin框架快速入门_第15张图片
我们还可以在定义结构体时添加tag标签,指定名称

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


五. 文件上传

文件上传也就是发http请求,下载也是如此

前端html代码

DOCTYPE html>
<html lang="en">
    <head>
        <title>user/indextitle>
    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>

单个文件上传

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")
}

然后运行测试,选择文件上传
Gin框架快速入门_第16张图片
可以看到上传成功
Gin框架快速入门_第17张图片
然后在当前项目目录下可以看到上传的图片
Gin框架快速入门_第18张图片


多个文件上传

前端页面修改为:

<!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>

后端修改为:

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")
}


六. 重定向

1️⃣ http重定向:使用Redirect,内部、外部重定向均支持

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

2️⃣ 路由重定向:使用HandleContext

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"})
})


七. 路由和路由组

1️⃣ 普通路由

基本的四种请求方式

//用于获取信息,类似于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) {
     ...})

可以匹配所有请求方式的Any

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

为没有配置处理函数的路由添加处理程序(默认情况下它返回404代码)

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

2️⃣ 路由组

我们可以将拥有共同URL前缀的路由划分为一个路由组。为了美观,习惯性一对{}包裹同组的路由,用不用{}包裹功能上没什么区别。路由组常用于在划分业务逻辑或划分API版本的情况

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()
}

由组也是支持嵌套的,例如:

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) {
     ...})
}


八. Gin中间件

Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件middleware,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等

Gin框架快速入门_第19张图片

中间件的定义

Gin框架中中间件必须是HandlerFunc类型,也就是func(*Context)类型,这是一种函数类型,必须有*Context参数

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

接下来自定义一个中间件Mymiddleware,来统计请求处理的耗时时间

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")
}

注意:

  • c.Next():表示调用该请求剩余的处理程序
  • c.Abort():表示阻止调用剩余的处理程序

注册中间件

1️⃣ 方式一:为单个路由注册

当我们通过c.Get或其他方式编写路由处理程序时,其中的第二个参数就是HandlerFunc类型的可变参数,因此自定义的中间件作为参数传入其中即可

// 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)
}

接下来我们编写一个路由处理程序,将上述定义的Mymiddleware中间件注册进去

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")
}

然后运行测试,访问localhost:9090/index,看后台打印信息,可以看到先通过了中间件再执行了路由处理函数
Gin框架快速入门_第20张图片

2️⃣ 为全局路由注册

如果我们有多个路由处理函数都想使用同一个中间件,每次传参有些麻烦,我们可以通过全局路由注册的方式,一处注册,多处生效

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")
}

运行测试,先后请求/index/index2,根据打印信息可以看到中间件均生效
Gin框架快速入门_第21张图片
如果再定义一个中间件Mymiddleware2,然后全局注册两个中间件

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",
		})
	})
}

再测试,访问/index,打印结果如下
Gin框架快速入门_第22张图片
对应程序的执行顺序如下如所示:
Gin框架快速入门_第23张图片

3️⃣ 为路由组注册中间件

方式一:

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

方式二:

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

中间件中存值

我们可以在中间件中存值,在路由处理函数中取到

Mymiddlerware2中间件中通过c.Set存入name值

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

在路由处理函数中可以通过c.Get/c.MustGet等函数取出name值

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

测试结果:
Gin框架快速入门_第24张图片


拓展

1️⃣ gin默认中间件

gin.Default()默认使用了LoggerRecovery中间件

// 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
}

其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由

2️⃣ gin中间件中使用goroutine

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy()

3️⃣ 通用中间件模板

以用户认证为例,通常我们会把中间件写成闭包的形式,然后可以在其中添加一些具体的操作

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

你可能感兴趣的:(Gin,go)