Go学习第十七章——Gin中间件与路由

news2024/10/6 18:31:48

Go web框架——Gin中间件与路由

    • 1 单独注册中间件
      • 1.1 入门案例
      • 1.2 多个中间件
      • 1.3 中间件拦截响应
      • 1.4 中间件放行
    • 2 全局注册中间件
    • 3 自定义参数传递
    • 4 路由分组
      • 4.1 入门案例
      • 4.2 路由分组注册中间件
      • 4.3 综合使用
    • 5 使用内置的中间件
    • 6 中间件案例
      • 权限验证
      • 耗时统计

1 单独注册中间件

Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等
即比如,如果访问一个网页的话,不管访问什么路径都需要进行登录,此时就需要为所有路径的处理函数进行统一一个中间件

Gin中的中间件必须是一个gin.HandlerFunc类型

1.1 入门案例

我们先看一下Get函数能够接收的参数:

func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodGet, relativePath, handlers)
}

type HandlerFunc func(*Context)

从这个函数里,我们能看到它可以,它可以接收很多的HandlerFunc类型的数据,并且发现是func(*Context)数据类型都可以,所以,我们可以定义很多个中间件,它必须是*gin.Context类型

  1. 定义一个中间件
func m1(c *gin.Context) {
    fmt.Println("m1 in.........")
}
  1. 写一个函数,在前端响应的数据,作为参考
func indexHandler(c *gin.Context) {
    fmt.Println("index.....")
    c.JSON(http.StatusOK, gin.H{
       "msg": "index",
    })
}
  1. 完整代码
func indexHandler(c *gin.Context) {
    fmt.Println("index.....")
    c.JSON(http.StatusOK, gin.H{
       "msg": "index",
    })
}

// 定义一个中间件
func m1(c *gin.Context) {
    fmt.Println("m1 in.........")
}

func main() {
    r := gin.Default()
    //m1处于indexHandler函数的前面,请求来之后,先走m1,再走index
    r.GET("/index", m1, indexHandler)

    r.Run(":8000")
}

注意:m1处于indexHandler函数的前面,请求来之后,先走m1,再走index

然后,使用游览器访问对应的请求,看一下输出:

image-20231028220548688

验证完毕!!~

1.2 多个中间件

router.GET,后面可以跟很多HandlerFunc方法,这些方法其实都可以叫中间件

package main

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

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...")
    c.JSON(200, gin.H{"msg": "响应数据"})
  }, m2)

  router.Run(":8080")
}

这里就不演示了~~

1.3 中间件拦截响应

c.Abort()函数,作用:拦截,后续的HandlerFunc就不会执行了

package main

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

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.JSON(200, gin.H{"msg": "第一个中间件拦截了"})
  c.Abort()
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...")
    c.JSON(200, gin.H{"msg": "响应数据"})
  }, m2)

  router.Run(":8080")
}

运行后,去游览器试一下,会发现只输出:m1 …in,也就是被第一个拦截器拦截了~

1.4 中间件放行

c.Next()函数,作用:Next前后形成了其他语言中的请求中间件和响应中间件

package main

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

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.Next()
  fmt.Println("m1 ...out")
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
  c.Next()
  fmt.Println("m2 ...out")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...in")
    c.JSON(200, gin.H{"msg": "响应数据"})
    c.Next()
    fmt.Println("index ...out")
  }, m2)

  router.Run(":8080")
}

输出结果:

m1 ...in
index ...in 
m2 ...in    
m2 ...out   
index ...out
m1 ...out   

输出的方式,有点像栈,先进去的m1...out最后再输出~

2 全局注册中间件

在Gin框架中,可以使用Use方法注册全局中间件。全局中间件会对所有的请求都生效。

func main() {
    r := gin.Default()

    r.Use(Logger()) // 注册全局中间件

    r.GET("/hello", HelloHandler)

    r.Run(":8080")
}

// Logger 是一个全局中间件
func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()

        c.Next()

        end := time.Now()
        latency := end.Sub(start)

        log.Printf("[%s] %s %s %v", c.Request.Method, c.Request.URL.Path, c.Request.RemoteAddr, latency)
    }
}

func HelloHandler(c *gin.Context) {
    c.String(http.StatusOK, "Hello, Gin!")
}

在上面的示例中,Logger函数是一个全局中间件。它在处理请求之前记录了请求的相关信息,并在请求处理完成后打印了请求的耗时。

3 自定义参数传递

在中间件之间传递自定义参数是一种常见的需求。在Gin框架中,可以使用Context.SetContext.Get方法来传递自定义参数,并且传递的数据是一个key-value

func main() {
    r := gin.Default()

    r.Use(AddCustomData("custom data"))

    r.GET("/hello", HelloHandler)

    r.Run(":8080")
}

func AddCustomData(data string) gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Set("customData", data) // 设置自定义参数

        c.Next()
    }
}

func HelloHandler(c *gin.Context) {
    customData, exists := c.Get("customData") // 获取自定义参数

    if exists {
        c.String(http.StatusOK, "Hello, Gin! Custom data: %s", customData)
    } else {
        c.String(http.StatusOK, "Hello, Gin!")
    }
}

在上面的示例中,AddCustomData函数返回了一个中间件函数,用于在Context中设置自定义参数。在HelloHandler处理函数中,通过Context.Get方法获取自定义参数并使用。

4 路由分组

4.1 入门案例

将一系列的路由放到一个组下,统一管理。例如,以下的路由前面统一加上api的前缀

package main

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

func main() {
  router := gin.Default()

  r := router.Group("/api")
  r.GET("/index", func(c *gin.Context) {
    c.String(200, "index")
  })
  r.GET("/home", func(c *gin.Context) {
    c.String(200, "home")
  })

  router.Run(":8080")
}

4.2 路由分组注册中间件

package main

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

func middle(c *gin.Context) {
  fmt.Println("middle ...in")
}

func main() {
  router := gin.Default()

  r := router.Group("/api").Use(middle)  // 可以链式,也可以直接r.Use(middle)
  r.GET("/index", func(c *gin.Context) {
    c.String(200, "index")
  })
  r.GET("/home", func(c *gin.Context) {
    c.String(200, "home")
  })

  router.Run(":8080")
}

这样写我们就可以指定哪一些分组下可以使用中间件了

当然,中间件还有一种写法,就是使用函数加括号的形式

package main

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

func middle(c *gin.Context) {
  fmt.Println("middle ...in")
}
func middle1() gin.HandlerFunc {
  // 这里的代码是程序一开始就会执行
  return func(c *gin.Context) {
    // 这里是请求来了才会执行
    fmt.Println("middle1 ...inin")
  }
}

func main() {
  router := gin.Default()

  r := router.Group("/api").Use(middle, middle1())
  r.GET("/index", func(c *gin.Context) {
    c.String(200, "index")
  })
  r.GET("/home", func(c *gin.Context) {
    c.String(200, "home")
  })

  router.Run(":8080")
}

4.3 综合使用

设置了一个中间件进行身份验证,并且还对路由进行分组,分为两组来使用

func main() {
    r := gin.Default()

    r.Use(Logger()) // 注册全局中间件

    v1 := r.Group("/v1")
    v1.Use(Auth()) // 注册 v1 路由组的局部中间件
    {
        v1.GET("/hello", HelloHandler)
        v1.GET("/user", UserHandler)
    }

    r.GET("/hello", HelloHandler) // 其他路由不使用 Auth 中间件

    r.Run(":8080")
}

func Auth() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 进行身份验证的逻辑

        if IsAuthenticated {
            c.Next()
        } else {
            c.AbortWithStatus(http.StatusUnauthorized)
        }
    }
}

func HelloHandler(c *gin.Context) {
    c.String(http.StatusOK, "Hello, Gin!")
}

func UserHandler(c *gin.Context) {
    c.String(http.StatusOK, "User Info")
}

// Logger 是一个全局中间件
func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()

		c.Next()

		end := time.Now()
		latency := end.Sub(start)

		log.Printf("[%s] %s %s %v", c.Request.Method, c.Request.URL.Path, c.Request.RemoteAddr, latency)
	}
}

5 使用内置的中间件

Gin框架内置了一些常用的中间件,可以直接使用。以下是一些常用的内置中间件和示例:

  • gin.Logger():记录请求日志
  • gin.Recovery():处理请求时的恢复机制
func main() {
    r := gin.Default()

    r.Use(gin.Logger()) // 使用 gin.Logger() 中间件
    r.Use(gin.Recovery()) // 使用 gin.Recovery() 中间件

    r.GET("/hello", HelloHandler)

    r.Run(":8000")
}

func HelloHandler(c *gin.Context) {
    c.String(http.StatusOK, "Hello, Gin!")
}

不过,平常我们不使用这两个中间件的时候,一样会有记录,所以我们看一下gin.Default函数

func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

从这段代码就可以看出,是默认使用了这两个函数,一般自动调用了~~~

6 中间件案例

权限验证

以前后端最流行的jwt为例,如果用户登录了,前端发来的每一次请求都会在请求头上携带上token

后台拿到这个token进行校验,验证是否过期,是否非法

如果通过就说明这个用户是登录过的

不通过就说明用户没有登录

package main

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

func JwtTokenMiddleware(c *gin.Context) {
  // 获取请求头的token
  token := c.GetHeader("token")
  // 调用jwt的验证函数
  if token == "1234" {
    // 验证通过
    c.Next()
    return
  }
  // 验证不通过
  c.JSON(200, gin.H{"msg": "权限验证失败"})
  c.Abort()
}

func main() {
  router := gin.Default()

  api := router.Group("/api")

  apiUser := api.Group("")
  {
    apiUser.POST("login", func(c *gin.Context) {
      c.JSON(200, gin.H{"msg": "登录成功"})
    })
  }
  apiHome := api.Group("system").Use(JwtTokenMiddleware)
  {
    apiHome.GET("/index", func(c *gin.Context) {
      c.String(200, "index")
    })
    apiHome.GET("/home", func(c *gin.Context) {
      c.String(200, "home")
    })
  }

  router.Run(":8080")
}

耗时统计

统计每一个视图函数的执行时间

func TimeMiddleware(c *gin.Context) {
  startTime := time.Now()
  c.Next()
  since := time.Since(startTime)
  // 获取当前请求所对应的函数
  f := c.HandlerName()
  fmt.Printf("函数 %s 耗时 %d\n", f, since)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1148621.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java项目之网络考试系统

视频教程: 01-创建数据库_哔哩哔哩_bilibili 源码下载:百度网盘 请输入提取码 准备工作 创建数据库配置IDEA后端导入前端 前言: 把代码掰开写进博客里,主要是让自己在整理笔记的过程中,多去思考完成这个功能的核心…

基于深度学习的单图像人群计数研究:网络设计、损失函数和监控信号

摘要 https://arxiv.org/pdf/2012.15685v2.pdf 单图像人群计数是一个具有挑战性的计算机视觉问题,在公共安全、城市规划、交通管理等领域有着广泛的应用。近年来,随着深度学习技术的发展,人群计数引起了广泛的关注并取得了巨大的成功。通过系统地回顾和总结2015年以来基于深…

rust学习——智能指针Rc

文章目录 Rc 与 ArcRcRc::clone观察引用计数的变化不可变引用一个综合例子Rc 简单总结 多线程无力的 RcArcArc 的性能损耗 总结 Rc 与 Arc Rust 所有权机制要求一个值只能有一个所有者,在大多数情况下,都没有问题,但是考虑以下情况&#xff1…

二维码智慧门牌管理系统升级解决方案:采集要素为智慧城市建设提供精准数据支持

文章目录 前言一、二维码智慧门牌管理系统的升级需求二、采集要素在系统升级中的应用三、消防栓、井盖等采集要素的应用 前言 随着城市化进程的加速,智慧城市的建设已成为未来城市发展的必然趋势。其中,二维码智慧门牌管理系统作为智慧城市的重要组成部…

基于Spring Boot的大学课程排课系统设计与实现

摘 要 大学课程排课是现代教育管理中重要的一环。目前,传统的排课方式已经无法满足日益增长的课程需求和学生个性化的诉求。因此,研究一种基于遗传算法的大学课程排课系统是非常必要的。本研究旨在开发一种基于SpringBoot Vue的大学课程排课系统&#x…

【Java 进阶篇】在Java Web应用中获取ServletContext对象详解

在Java Web应用开发中,ServletContext对象扮演着重要的角色,它允许你在整个Web应用程序中存储和共享数据。ServletContext对象是Servlet容器提供的一种用于管理Web应用程序的全局信息的方式。本文将详细探讨ServletContext对象的概念、用途以及如何在Jav…

算法笔记【8】-合并排序算法

文章目录 一、前言二、合并排序算法基本原理三、实现步骤四、优缺点分析 一、前言 合并排序算法通过采用分治策略和递归思想,实现了高效、稳定的排序功能。本文将深入探讨合并排序算法的原理、实现步骤,并讨论其优缺点。 二、合并排序算法基本原理 合…

AntDB数据库荣获 “2023年信创物联网优秀服务商”

日前,在2023世界数字经济大会暨第十三届智博会 2023京甬信创物联网产融对接会上,AntDB数据库再获殊荣,获评“2023年信创物联网优秀服务商”。 图1:2023年信创物联网优秀服务商颁奖现场 信创物联网是信息技术应用创新与物联网的结…

网络爬虫入门导学

一、内容组织 2、常用的python IDE工具 比较推荐以下几种: 其中IDLE是python自带的/默认的/常用的/入门级编写工具,包含交互式和文件式 适用于:简单直接/入门级/代码不超过300行 Sublime Text是专为程序员开发的第三方专用编程工具&#xff…

OPNET <<< Program Abort >>> Standard function stack imbalance

OPNET <<< Program Abort >>> Standard function stack imbalance OPNET 问题原因及解决办法 OPNET 问题 OPNET仿真时遇到此问题&#xff1a; <<< Program Abort >>> Standard function stack imbalance 原因及解决办法 出现此问题是因…

【逗老师的无线电】艾德克斯ITECH电源电子负载网口适配器

艾德克斯的产品还是不错的&#xff0c;但是ITECH的大部分中低端设备都不带网口&#xff0c;只带了一个串口&#xff0c;并且这个串口还是个完全非标定义的5V TTL串口&#xff0c;原装的适配器300多还只能转接成RS-232。 那么&#xff0c;这回咱们来整个骚活&#xff0c;直接给艾…

Go-Python-Java-C-LeetCode高分解法-第十二周合集

前言 本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接&#xff1a;LeetCode-Go-Python-Java-C 欢迎订阅CSDN专栏&#xff0c;每日一题&#xff0c;和博主一起进步 LeetCode专栏 我搜集到了50道精选题&#xff0c;适合速成概览大部分常用算法 突…

简单明了!网关Gateway路由配置filters实现路径重写及对应正则表达式的解析

问题背景&#xff1a; 前端需要发送一个这样的请求&#xff0c;但出现404 首先解析请求的变化&#xff1a; http://www.51xuecheng.cn/api/checkcode/pic 1.请求先打在nginx&#xff0c;www.51xuecheng.cn/api/checkcode/pic部分匹配到了之后会转发给网关进行处理变成localho…

人工智能-线性回归的从零开始实现

线性回归的从零开始实现 在了解线性回归的关键思想之后&#xff0c;我们可以开始通过代码来动手实现线性回归了。 在这一节中&#xff0c;我们将从零开始实现整个方法&#xff0c; 包括数据流水线、模型、损失函数和小批量随机梯度下降优化器。 虽然现代的深度学习框架几乎可以…

预安装win11的电脑怎么退回正版win10?

对于新购的笔记本 通常来讲预装的系统是全新安装的&#xff0c;是没有之前Windows10系统文件的&#xff0c;无法回退。 可以打开设置-----系统----恢复-----看下是否有该选项。 ------------------------------------------------------------------------------- 若是在上述…

[论文精读]How Powerful are Graph Neural Networks?

论文原文&#xff1a;[1810.00826] How Powerful are Graph Neural Networks? (arxiv.org) 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#x…

字符串固定长度自动补齐的主要方法

1 问题 输入日期例如02/03/04时&#xff0c;要求输出2002年03月04日、2004年02月03日或2004年03月04日&#xff0c;但是经过一系列处理后0会被自动处理掉&#xff0c;例如输出2002年3月4日等&#xff0c;与要求输出月、日必须是两位数不符。 2 方法 要自动补充“0”&#xff0c…

<学习笔记>从零开始自学Python-之-常用库篇(十三)内置小型数据库shelve

一、shelve简介&#xff1a; shelve是Python当中数据储存的方案&#xff0c;类似key-value数据库&#xff0c;便于保存Python对象&#xff0c;shelve只有一个open&#xff08;&#xff09;函数&#xff0c;用来打开指定的文件&#xff08;字典&#xff09;&#xff0c;会返回一…

CMake 构建指南:如何提高 C/C++ 项目的可维护性

如果您是一位C/C开发人员&#xff0c;那么您一定知道在编写和维护大型项目时所面临的挑战。这些项目通常包含大量的源代码、库和依赖项&#xff0c;需要耗费大量的时间和精力才能构建和维护。在这种情况下&#xff0c;使用自动化工具可以大大减轻您的负担&#xff0c;提高项目的…

线段树 区间赋值 + 区间加减 + 求区间最值

线段树好题&#xff1a;P1253 扶苏的问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 区间赋值 区间加减 求区间最大。 对于区间赋值和区间加减来说&#xff0c;需要两个懒标记&#xff0c;一个表示赋值cover&#xff0c;一个表示加减add。 区间赋值的优先级大于区间加…