Salmon的全栈知识 Salmon的全栈知识
首页
  • JavaSE
  • JavaWeb
  • Spring生态
  • JUC
  • JVM
  • Netty
  • Java各版本特性
  • 23种设计模式
  • Maven
  • Java常用框架
  • Dubbo
  • OpenFeign
  • Nacos
  • Zookeeper
  • Sentinel
  • Seata
  • Gateway
  • Go基础
  • Gin
  • SQL数据库

    • MySQL
    • Oracle
  • NoSQL数据库

    • Redis
    • MongoDB
    • ElasticSearch
  • 消息中间件

    • RabbitMQ
    • RocketMQ
    • Kafka
    • ActiveMQ
    • MQTT
    • NATS
  • 网关中间件

    • Nginx
  • Linux
  • Docker
  • Git
  • K8s
  • Solidity
  • Java
  • 计算机网络
  • 操作系统
GitHub (opens new window)
首页
  • JavaSE
  • JavaWeb
  • Spring生态
  • JUC
  • JVM
  • Netty
  • Java各版本特性
  • 23种设计模式
  • Maven
  • Java常用框架
  • Dubbo
  • OpenFeign
  • Nacos
  • Zookeeper
  • Sentinel
  • Seata
  • Gateway
  • Go基础
  • Gin
  • SQL数据库

    • MySQL
    • Oracle
  • NoSQL数据库

    • Redis
    • MongoDB
    • ElasticSearch
  • 消息中间件

    • RabbitMQ
    • RocketMQ
    • Kafka
    • ActiveMQ
    • MQTT
    • NATS
  • 网关中间件

    • Nginx
  • Linux
  • Docker
  • Git
  • K8s
  • Solidity
  • Java
  • 计算机网络
  • 操作系统
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Gin 介绍
  • Gin 环境搭建
  • golang 程序的热加载
  • Gin 框架中的路由
  • Gin HTML 模板渲染
  • 静态文件服务
  • 路由详解
  • Gin 中自定义控制器
  • Gin 中间件
    • 1、路由中间件
      • 1.1、初识中间件
      • 1.2、ctx.Next()调用该请求的剩余处理程序
      • 1.3、一个路由配置多个中间件的执行顺序
      • 1.4、 c.Abort()--(了解)
    • 2、全局中间件
    • 3、在路由分组中配置中间件
    • 4、中间件和对应控制器之间共享数据
    • 5、中间件注意事项
  • Gin 中自定义 Model
  • Gin 文件上传
  • Gin 中的 Cookie
  • Gin 中的 Session
  • Gin 中使用 GORM 操作 mysql 数据库
  • 原生 SQL 和 SQL 生成器
  • Gin 中使用 GORM 实现表关联查询
  • GORM 中使用事务
  • Gin 中使用 go-ini 加载.ini 配置文件
  • 《Gin》笔记
Salmon
2025-04-03
目录

Gin 中间件

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

通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作。

# 1、路由中间件

# 1.1、初识中间件

Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函数,最后一个 func 回调函数前面触发的方法都可以称为中间件。

package main

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

func initMiddleware(ctx *gin.Context) {
    fmt.Println("我是一个中间件")
}
func main() {
    r := gin.Default()
    r.GET("/", initMiddleware, func(ctx *gin.Context) {
       ctx.String(200, "首页--中间件演示")
    })
    r.GET("/news", initMiddleware, func(ctx *gin.Context) {
       ctx.String(200, "新闻页面--中间件演示")
    })
    r.Run(":9090")
}

效果:

image-20250403165547603

# 1.2、ctx.Next()调用该请求的剩余处理程序

中间件里面加上 ctx.Next() 可以让我们在路由匹配完成后执行一些操作。

比如我们统计一个请求的执行时间。

package main

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

func initMiddleware(ctx *gin.Context) {
    fmt.Println("1-执行中中间件")
    start := time.Now().UnixNano()
    // 调用该请求的剩余处理程序
    ctx.Next()
    fmt.Println("3-程序执行完成 计算时间")
    // 计算耗时 Go 语言中的 Since()函数保留时间值,并用于评估与实际时间的差异
    end := time.Now().UnixNano()
    fmt.Println(end - start)
}
func main() {
    r := gin.Default()
    r.GET("/", initMiddleware, func(ctx *gin.Context) {
       fmt.Println("2-执行首页返回数据")
       ctx.String(200, "首页--中间件演示")
    })
    r.GET("/news", initMiddleware, func(ctx *gin.Context) {
       ctx.String(200, "新闻页面--中间件演示")
    })
    r.Run(":9090")
}

效果:

image-20250403165748689

# 1.3、一个路由配置多个中间件的执行顺序

package main

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

func initMiddlewareOne(ctx *gin.Context) {
    fmt.Println("initMiddlewareOne--1-执行中中间件")
    // 调用该请求的剩余处理程序
    ctx.Next()
    fmt.Println("initMiddlewareOne--2-执行中中间件")
}

func initMiddlewareTwo(ctx *gin.Context) {
    fmt.Println("initMiddlewareTwo--1-执行中中间件")
    // 调用该请求的剩余处理程序
    ctx.Next()
    fmt.Println("initMiddlewareTwo--2-执行中中间件")
}

func main() {
    r := gin.Default()
    r.GET("/news", initMiddlewareOne, initMiddlewareTwo, func(ctx *gin.Context) {
       fmt.Println("执行路由里面的程序")
       ctx.String(200, "新闻页面--中间件演示")
    })
    r.Run(":9090")
}

效果:

image-20250403170101292

# 1.4、 c.Abort()--(了解)

Abort 是终止的意思, c.Abort() 表示终止调用该请求的剩余处理程序

package main

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

func initMiddlewareOne(ctx *gin.Context) {
    fmt.Println("initMiddlewareOne--1-执行中中间件")
    // 调用该请求的剩余处理程序
    ctx.Next()
    fmt.Println("initMiddlewareOne--2-执行中中间件")
}

func initMiddlewareTwo(ctx *gin.Context) {
    fmt.Println("initMiddlewareTwo--1-执行中中间件")
    // 终止调用该请求的剩余处理程序
    ctx.Abort()
    fmt.Println("initMiddlewareTwo--2-执行中中间件")
}

func main() {
    r := gin.Default()
    r.GET("/news", initMiddlewareOne, initMiddlewareTwo, func(ctx *gin.Context) {
       fmt.Println("执行路由里面的程序")
       ctx.String(200, "新闻页面--中间件演示")
    })
    r.Run(":9090")
}

效果:

image-20250403170321042

# 2、全局中间件

package main

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

func initMiddleware(ctx *gin.Context) {
    fmt.Println("全局中间件 通过 r.Use 配置")
    // 调用该请求的剩余处理程序
    ctx.Next()
}

func main() {
    r := gin.Default()
    r.Use(initMiddleware)
    r.GET("/", func(ctx *gin.Context) {
       ctx.String(200, "首页--中间件演示")
    })
    r.GET("/news", func(ctx *gin.Context) {
       ctx.String(200, "新闻页面--中间件演示")
    })
    r.Run(":9090")
}

效果:

image-20250403171007318

# 3、在路由分组中配置中间件

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

  • 写法 1:
shopGroup := r.Group("/shop", StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ... 
}
  • 写法 2:
shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ... 
}

2、分组路由 AdminRoutes.go 中配置中间件

package routes

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "test4/controller/admin"
)

func initMiddleware(ctx *gin.Context) {
    fmt.Println("路由分组中间件")
    // 调用该请求的剩余处理程序
    ctx.Next()
}

func AdminRoutesInit(router *gin.Engine) {
    adminRouter := router.Group("/admin", initMiddleware)
    {
       adminRouter.GET("/user", admin.UserController{}.Index)
       adminRouter.GET("/user/add", admin.UserController{}.Add)
       adminRouter.GET("/news", admin.NewsController{}.Index)
    }
}

# 4、中间件和对应控制器之间共享数据

设置值

ctx.Set("username", "张三")

获取值

username, _ := ctx.Get("username")

中间件设置值

func InitAdminMiddleware(ctx *gin.Context) {
    fmt.Println("路由分组中间件")
    // 可以通过 ctx.Set 在请求上下文中设置值,后续的处理函数能够取到该值
    ctx.Set("username", "张三")
}

func AdminRoutesInit(router *gin.Engine) {
    adminRouter := router.Group("/admin", InitAdminMiddleware)
    {
       adminRouter.GET("/user", admin.UserController{}.Index)
       adminRouter.GET("/user/add", admin.UserController{}.Add)
       adminRouter.GET("/news", admin.NewsController{}.Index)
    }
}

控制器获取值

package admin

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

type UserController struct {
}

func (u UserController) Index(ctx *gin.Context) {
    username, _ := ctx.Get("username")
    fmt.Println(username)
    ctx.String(http.StatusOK, "这是用户首页 111")
}

func (u UserController) Add(ctx *gin.Context) {
	ctx.String(http.StatusOK, "增加用户")
}

效果:

image-20250403172233726

# 5、中间件注意事项

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

r.GET("/copy", func(c *gin.Context) {
    cCp := c.Copy()
    go func() {
       // simulate a long task with time.Sleep(). 5 seconds
       time.Sleep(5 * time.Second)
       // 这里使用你创建的副本
       fmt.Println("Done! in path " + cCp.Request.URL.Path)
    }()
    c.String(200, "首页")
})

效果:

image-20250403172656615

上次更新: 2025/04/05, 15:44:20
Gin 中自定义控制器
Gin 中自定义 Model

← Gin 中自定义控制器 Gin 中自定义 Model→

Theme by Vdoing | Copyright © 2022-2025 Salmon's Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式