乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

    • Golang基础知识

    • Golang进阶知识

    • Golang常用包

    • Gin框架

      • 安装
      • gin路由
      • 请求数据参数绑定
      • gin渲染
      • 使用模板渲染
      • 静态文件的使用
      • 数据渲染
      • gin重定向
      • gin同步和异步
      • go中间件
        • 会话保持
        • 文件上传
        • JWT的简单使用
        • 模板函数
        • Swagger
        • API访问控制
        • 常见的应用中间件
        • 应用配置管理
        • 优雅停止与重启
        • 集成Casbin进行访问权限控制
    • AIOps

    • Python

    • DevOps

    • 专栏
    • Golang
    • Gin框架
    乔克
    2025-07-19
    目录

    go中间件

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

    # 定义中间件

    Gin 中的中间件必须是一个gin.HandlerFunc类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。

    func MyMiddleWare()(gin.HandlerFunc){
    	return func(ctx *gin.Context){
    		fmt.Println("中间件开始......")
    		ctx.Set("name","joker")
    		start := time.Now()
    		// 调用Next()处理函数
    		ctx.Next()
    		// 计算函数耗时多少
    		cost := time.Since(start)
    		fmt.Println("耗时:",cost)
    	}
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    其中最关键的一点是ctx.Next(),调用这个的作用就是处理后续的处理函数。

    # 注册中间件

    在 gin 框架中,我们可以为每个路由添加任意数量的中间件。

    # 全局中间件

    定义全局中间件的话就在主函数中用User()方法加载中间件。为了代码规范,建议绑定路由规则都包含在{}中,如下:

    func main(){
    	// 1、创建路由
    	g := gin.Default()
    	// 2、加载中间件
    	g.Use(MyMiddleWare())
    	{
    		// 2、绑定路由规则
    		g.GET("/middleware",myFunc)
    	}
    	g.Run(":8000")
    }
    
    func myFunc(context *gin.Context){
    	// 获取中间件中设置的值
    	ret := context.MustGet("name")
    	fmt.Println("中间件中的值:",ret)
    	time.Sleep(time.Second*5)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    然后访问输出如下:

    中间件开始......
    中间件中的值: joker
    耗时: 5.001355s
    [GIN] 2020/04/02 - 18:19:56 |?[97;42m 200 ?[0m|    5.0050022s |       127.0.0.1 |?[97;44m GET     ?[0m "/middleware"
    
    1
    2
    3
    4

    # 局部中间件

    局部中间件也就是为某个路由单独注册中间件,只需要在绑定路由的时候在路径后面跟上中间件函数即可。

    如下:

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"time"
    )
    
    func MyMiddleWare()(gin.HandlerFunc){
    	return func(ctx *gin.Context){
    		fmt.Println("中间件开始......")
    		ctx.Set("name","joker")
    		start := time.Now()
    		// 调用Next()处理函数
    		ctx.Next()
    		// 计算函数耗时多少
    		cost := time.Since(start)
    		fmt.Println("耗时:",cost)
    	}
    }
    
    func main(){
    	// 1、创建路由
    	g := gin.Default()
    	// 2、加载中间件
    	g.Use(MyMiddleWare())
    	{
    		// 2、绑定路由规则
    		g.GET("/test2",MyMiddleWare(),test2)
    	}
    	g.Run(":8000")
    }
    
    func test2(ctx *gin.Context){
    	// 获取中间件中设置的值
    	ret := ctx.MustGet("name")
    	fmt.Println("中间件中的值:",ret)
    	time.Sleep(time.Second*5)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39

    然后访问如下:

    中间件开始......
    中间件开始......
    中间件中的值: joker
    耗时: 5.0002031s
    耗时: 5.0007657s
    [GIN] 2020/04/02 - 18:40:55 |?[97;42m 200 ?[0m|    5.0037571s |       127.0.0.1 |?[97;44m GET     ?[0m "/test2"
    
    1
    2
    3
    4
    5
    6

    我们可以看到执行了两遍中间件,其中在执行函数之前是先执行的全局中间件,然后再是局部中间件,再返回的时候先执行局部中间件,再执行全局中间件。

    流程图如下:

    8c3d753534e69925df185a752ed8771e MD5

    # 为路由组注册中间件

    为路由组注册中间件有以下两种写法。

    写法 1:

    shopGroup := r.Group("/shop", StatCost())
    {
        shopGroup.GET("/index", func(c *gin.Context) {...})
        ...
    }
    
    1
    2
    3
    4
    5

    写法 2:

    shopGroup := r.Group("/shop")
    shopGroup.Use(StatCost())
    {
        shopGroup.GET("/index", func(c *gin.Context) {...})
        ...
    }
    
    1
    2
    3
    4
    5
    6

    # 中间件注意事项

    # gin 默认中间件

    gin.Default()默认使用了Logger和Recovery中间件,其中:

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

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

    # gin 中间件中使用 goroutine

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

    作者:乔克

    本文链接:https://jokerbai.com

    版权声明:本博客所有文章除特别声明外,均采用 署名-非商业性-相同方式共享 4.0 国际 (CC-BY-NC-SA-4.0) 许可协议。转载请注明出处!

    上次更新: 2025/07/19, 09:17:41
    gin同步和异步
    会话保持

    ← gin同步和异步 会话保持→

    最近更新
    01
    使用 Generic Webhook Trigger 触发 Jenkins 多分支流水线自动化构建
    07-19
    02
    使用Zadig从0到1实现持续交付平台
    07-19
    03
    基于Jira的运维发布平台
    07-19
    更多文章>
    Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式