• Gin路由中间件详解


    什么是中间件

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

    中间件操作演示

    方法一:

    直接写在func,回调函数

    1. r.GET("/middle",func(ctx *gin.Context) {
    2. fmt.Print("我是中间件--------")
    3. },func(ctx *gin.Context) {
    4. ctx.String(http.StatusOK,"中间件测试")
    5. })

    方法二:

    在外部构造方法

    1. package main
    2. import (
    3. "fmt"
    4. "gindemo/route"
    5. "net/http"
    6. "github.com/gin-gonic/gin"
    7. )
    8. //中间件测试二,在外写方法
    9. func testMiddle (ctx *gin.Context){
    10. fmt.Println("卫宫士郎")
    11. }
    12. func main() {
    13. // 创建一个默认的路由引擎
    14. r := gin.Default()
    15. //中间件测试二,回调函数在外面写
    16. r.GET("/test",testMiddle,func(ctx *gin.Context) {
    17. ctx.String(http.StatusOK,"方法写在外面的中间件测试")
    18. })
    19. r.Run(":8210")
    20. }

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

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

    1. package main
    2. import (
    3. "fmt"
    4. "gindemo/route"
    5. "net/http"
    6. "github.com/gin-gonic/gin"
    7. )
    8. func nextMiddle (ctx *gin.Context){
    9. fmt.Println("next调用执行前")
    10. ctx.Next()
    11. fmt.Println("next调用执行后---")
    12. }
    13. func main() {
    14. // 创建一个默认的路由引擎
    15. r := gin.Default()
    16. //ctx.Next调用该请求的剩余处理程序
    17. r.GET("/next",nextMiddle,func(ctx *gin.Context) {
    18. fmt.Println("路由内的内容")
    19. },func(ctx *gin.Context) {
    20. ctx.String(http.StatusOK,"next调用")
    21. })
    22. r.Run(":8210")
    23. }
    比如统计一个请求的执行时间
    1. func nextMiddle (ctx *gin.Context){
    2. begin := time.Now().UnixNano()
    3. fmt.Println("next调用执行前")
    4. ctx.Next()
    5. fmt.Println("next调用执行后---")
    6. end := time.Now().UnixNano()
    7. fmt.Println("执行时间为:",end-begin)
    8. }

    只需修改方法即可,然后进行访问

    看回显结果

     那么有个问题,如果配置多个中间件,加上next,它的执行顺序是什么样的

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

    1. package main
    2. import (
    3. "fmt"
    4. "gindemo/route"
    5. "net/http"
    6. "github.com/gin-gonic/gin"
    7. )
    8. func nextMiddle (ctx *gin.Context){
    9. fmt.Println("next调用执行前---nextmiddle--1")
    10. ctx.Next()
    11. fmt.Println("next调用执行后---nextmiddle--1")
    12. }
    13. func nextMiddle2 (ctx *gin.Context){
    14. fmt.Println("next调用执行前---nextmiddle--2")
    15. ctx.Next()
    16. fmt.Println("next调用执行后---nextmiddle--2")
    17. }
    18. func main() {
    19. // 创建一个默认的路由引擎
    20. r := gin.Default()
    21. //ctx.Next调用该请求的剩余处理程序
    22. r.GET("/next",nextMiddle,nextMiddle2,func(ctx *gin.Context) {
    23. fmt.Println("路由内的内容")
    24. },func(ctx *gin.Context) {
    25. ctx.String(http.StatusOK,"next调用")
    26. })
    27. r.Run(":8210")
    28. }

    先头部(next前的两个下来) ,再逆序回去

    思考一个问题,如果路由配置许多的话,我们需要去每个都配置中间件的话,有没有一个方法可以一键配置

    全局中间件

    路由.use()

    1. package main
    2. import (
    3. "fmt"
    4. "gindemo/route"
    5. "net/http"
    6. "github.com/gin-gonic/gin"
    7. )
    8. func nextMiddle (ctx *gin.Context){
    9. fmt.Println("next调用执行前---nextmiddle--1")
    10. ctx.Next()
    11. fmt.Println("next调用执行后---nextmiddle--1")
    12. }
    13. func nextMiddle2 (ctx *gin.Context){
    14. fmt.Println("next调用执行前---nextmiddle--2")
    15. ctx.Next()
    16. fmt.Println("next调用执行后---nextmiddle--2")
    17. }
    18. func main() {
    19. // 创建一个默认的路由引擎
    20. r := gin.Default()
    21. r.Use(nextMiddle,nextMiddle2)
    22. r.GET("/test",func(ctx *gin.Context) {
    23. ctx.String(http.StatusOK,"方法写在外面的中间件测试")
    24. })
    25. //ctx.Next调用该请求的剩余处理程序
    26. r.GET("/next",func(ctx *gin.Context) {
    27. fmt.Println("路由内的内容")
    28. },func(ctx *gin.Context) {
    29. ctx.String(http.StatusOK,"next调用")
    30. })
    31. r.Run(":8210")
    32. }

    在路由分组中配置中间件 

    在路由分组中配置中间件有两种方法

    其一

    其二 

    我们依旧选择,在外部去写中间件

    外部写中间件

    创捷middle文件夹下,创建testmiddle,go文件

    使用apiRoute进行测试

     测试

     其他的类似backRoute....未配置的是不能受到中间件的反馈的

    那有没有一种方法可以使得中间件和对应控制器之间共享数据?

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

    在外部中间件内设置值

    点进去设置

    在控制器内获取值

    注意这里获取的值是空接口类型,如果要显示的话可以使用类型断言

     

    1. package apifunc
    2. import (
    3. "fmt"
    4. "net/http"
    5. "github.com/gin-gonic/gin"
    6. )
    7. //结构体的好处:可以让下面的方法可以访问结构体(父结构体内的方法)
    8. type ApiInfo struct{
    9. Jicheng
    10. }
    11. func (way ApiInfo) Mes(ctx *gin.Context) {
    12. ctx.String(http.StatusOK, "Api数据回显--抽离方法---结构体!!!");
    13. way.Success(ctx);
    14. way.Fatel(ctx);
    15. }
    16. func (way ApiInfo) Control(ctx *gin.Context) {
    17. // ctx.String(http.StatusOK, "Api数据控制--抽离方法---结构体")
    18. //获取值
    19. username,_ := ctx.Get("username")
    20. fmt.Println(username)
    21. //类型断言
    22. v,ok := username.(string)
    23. if ok == true{
    24. ctx.String(http.StatusOK, "Api数据控制--抽离方法---结构体---"+v)
    25. }else{
    26. ctx.String(http.StatusOK, "Api数据控制--抽离方法---结构体")
    27. }
    28. }

     测试

    注意事项:

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

    测试如下:

    外部中间件配置:

    ClientIP:ClientIP首先读取X-Forwarded-Forheader 中用,分隔的第一个ip地址,如果这个地址不存在,就会从X-Real-Ipheader 中获取,如果还是不存在,说明流量并非是由反向代理转发而来,而是客户端直接请求服务,这时通过http.Request.RemoteAddr字段截取除去端口号的 ip 地址

    路由配置:

    main.go :
    1. package main
    2. import (
    3. "fmt"
    4. "gindemo/route"
    5. "github.com/gin-gonic/gin"
    6. )
    7. //中间件测试二,在外写方法
    8. func testMiddle (ctx *gin.Context){
    9. fmt.Println("卫宫士郎")
    10. }
    11. func nextMiddle (ctx *gin.Context){
    12. fmt.Println("next调用执行前---nextmiddle--1")
    13. ctx.Next()
    14. fmt.Println("next调用执行后---nextmiddle--1")
    15. }
    16. func nextMiddle2 (ctx *gin.Context){
    17. fmt.Println("next调用执行前---nextmiddle--2")
    18. ctx.Next()
    19. fmt.Println("next调用执行后---nextmiddle--2")
    20. }
    21. func main() {
    22. // 创建一个默认的路由引擎
    23. r := gin.Default()
    24. //加载模板 放在配置路由前面
    25. r.LoadHTMLGlob("templates/**/*")
    26. //配置静态web目录 第一个参数表示路由, 第二个参数表示映射的目录
    27. r.Static("/static", "./static")
    28. // 前置页面展示
    29. route.DefaultRoute(r)
    30. //接入接口展示模拟
    31. route.ApiRoute(r)
    32. //后台页面展示
    33. route.BackRoute(r)
    34. r.Run(":8210")
    35. }

  • 相关阅读:
    select并发服务器实现
    MySQL数据库索引练习
    Oauth2认证及Spring Security Oauth2授权码模式
    【TB作品】STM32F102C8T6单片机,PWM发生器
    java自学阶段二:JavaWeb开发60(mybatis学习)
    Netty优化-扩展自定义协议中的序列化算法
    C#类(Class)
    软件测试面试题:简述bug的生命周期?
    9.2.4 【MySQL】段的结构
    【Git】SourceTree通过ssh密钥克隆项目到本地
  • 原文地址:https://blog.csdn.net/m0_72264240/article/details/132854866