当前位置: 首页 > article >正文

Gin框架操作指南07:路由与中间件

官方文档地址(中文):https://gin-gonic.com/zh-cn/docs/
注:本教程采用工作区机制,所以一个项目下载了Gin框架,其余项目就无需重复下载,想了解的读者可阅读第一节:Gin操作指南:开山篇。
本节演示路由与中间件,包括路由参数;路由组;使用中间件;在中间件中使用Goroutine;自定义中间件。其中不使用默认的中间件很简单,就是使用
r := gin.New()代替r := gin.Default(),读者可自行演示。在开始之前,我们需要在”03路由与中间件“目录下打开命令行,执行如下命令来创建子目录:

mkdir 路由参数 路由组 使用中间件 在中间件中使用Goroutine 自定义中间件

目录

    • 一、路由参数
    • 二、路由组
    • 三、使用中间件
    • 四、在中间件中使用Goroutine
    • 五、自定义中间件

一、路由参数

package main

import (
	"net/http"

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

func main() {
	// 创建 Gin 路由实例
	router := gin.Default()

	// 路由匹配模式为 /user/:name
	// ":name" 是路由参数,可以匹配具体的用户名,例如 /user/john
	// 此 handler 将匹配像 /user/john 这样的 URL,但是不会匹配 /user/ 或者 /user
	router.GET("/user/:name", func(c *gin.Context) {
		// 使用 c.Param 获取路由中的参数 "name"
		name := c.Param("name")
		// 返回包含该 name 的字符串作为响应
		c.String(http.StatusOK, "Hello %s", name)
	})

	// 路由匹配模式为 /user/:name/*action
	// ":name" 是路由参数,可以匹配具体的用户名,例如 /user/john
	// "*action" 是通配符参数,表示匹配从指定路径开始的所有路径片段,例如 /user/john/send 或 /user/john/anything_else
	// 如果路径为 /user/john 并且没有其他路由匹配,Gin 会自动将其重定向到 /user/john/
	router.GET("/user/:name/*action", func(c *gin.Context) {
		// 获取路由参数 "name" 的值
		name := c.Param("name")
		// 获取通配符参数 "action" 的值,该值匹配路径中 "name" 后面的所有部分
		action := c.Param("action")
		// 拼接 name 和 action 作为响应的消息
		message := name + " is " + action
		// 返回拼接后的消息作为响应
		c.String(http.StatusOK, message)
	})

	// 启动 HTTP 服务器,监听 8080 端口
	router.Run(":8080")
}

效果
在这里插入图片描述

二、路由组

package main

import (
	"net/http"

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

// loginEndpoint 处理登录请求的示例处理函数
func loginEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"message": "Login successful"})
}

// submitEndpoint 处理提交请求的示例处理函数
func submitEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"message": "Submit successful"})
}

// readEndpoint 处理读取请求的示例处理函数
func readEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"message": "Read successful"})
}

func main() {
	// 创建一个默认的 Gin 路由器
	router := gin.Default()

	// 定义第一个路由组 "v1"。所有 v1 组中的路由将以 "/v1" 开头
	v1 := router.Group("/v1")
	{
		// v1 组下的 POST 请求路由,处理 /v1/login,调用 loginEndpoint 处理函数
		v1.POST("/login", loginEndpoint)

		// v1 组下的 POST 请求路由,处理 /v1/submit,调用 submitEndpoint 处理函数
		v1.POST("/submit", submitEndpoint)

		// v1 组下的 POST 请求路由,处理 /v1/read,调用 readEndpoint 处理函数
		v1.POST("/read", readEndpoint)
	}

	// 定义第二个路由组 "v2"。所有 v2 组中的路由将以 "/v2" 开头
	v2 := router.Group("/v2")
	{
		// v2 组下的 POST 请求路由,处理 /v2/login,调用 loginEndpoint 处理函数
		v2.POST("/login", loginEndpoint)

		// v2 组下的 POST 请求路由,处理 /v2/submit,调用 submitEndpoint 处理函数
		v2.POST("/submit", submitEndpoint)

		// v2 组下的 POST 请求路由,处理 /v2/read,调用 readEndpoint 处理函数
		v2.POST("/read", readEndpoint)
	}

	// 启动 HTTP 服务器,监听 8080 端口
	router.Run(":8080")
}

效果
在这里插入图片描述
在这里插入图片描述

三、使用中间件

package main

import (
	"log"
	"time"

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

// MyBenchLogger 是自定义的中间件,用于在路由上输出耗时信息
func MyBenchLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()

		// 继续处理请求
		c.Next()

		// 处理结束后,计算请求的耗时并输出日志
		latency := time.Since(t)
		log.Print(latency)
	}
}

// AuthRequired 是模拟的中间件,通常用于验证用户是否通过身份认证
func AuthRequired() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 这里可以编写检查用户身份的逻辑
		// 如果用户未通过身份验证,可以使用 `c.Abort()` 停止请求处理链
		log.Println("AuthRequired middleware executed")

		// 继续处理请求
		c.Next()
	}
}

// 模拟的处理器函数
func benchEndpoint(c *gin.Context) {
	// 这是实际的处理逻辑,返回一个简单的文本响应
	c.String(200, "Benchmark endpoint")
}

func loginEndpoint(c *gin.Context) {
	// 登录处理逻辑,返回一个简单的文本响应
	c.String(200, "Login successful")
}

func submitEndpoint(c *gin.Context) {
	// 表单提交处理逻辑,返回一个简单的文本响应
	c.String(200, "Form submitted")
}

func readEndpoint(c *gin.Context) {
	// 数据读取处理逻辑,返回一个简单的文本响应
	c.String(200, "Data read")
}

func analyticsEndpoint(c *gin.Context) {
	// 嵌套路由组处理逻辑,用于处理特定功能,例如数据分析
	c.String(200, "Analytics data")
}

func main() {
	// 新建一个没有任何默认中间件的路由
	r := gin.New()

	// 全局中间件
	// Logger 中间件会记录请求的日志,写入 gin.DefaultWriter (默认是 os.Stdout)
	r.Use(gin.Logger())

	// Recovery 中间件会自动捕获请求中的 panic,并返回 500 错误
	r.Use(gin.Recovery())

	// 可以为特定路由使用中间件,这里为 "/benchmark" 路由添加了自定义的 MyBenchLogger 中间件
	r.GET("/benchmark", MyBenchLogger(), benchEndpoint)

	// 认证路由组 authorized,通过 AuthRequired 中间件来控制访问权限
	authorized := r.Group("/")
	// 使用自定义的 AuthRequired 中间件来保护这个路由组
	authorized.Use(AuthRequired())
	{
		// 这些路由都属于 authorized 路由组,并且会执行 AuthRequired 中间件
		authorized.POST("/login", loginEndpoint)
		authorized.POST("/submit", submitEndpoint)
		authorized.POST("/read", readEndpoint)

		// 嵌套路由组 testing,所有在此路由组下的路由也会执行 AuthRequired 中间件
		testing := authorized.Group("testing")
		testing.GET("/analytics", analyticsEndpoint)
	}

	// 启动 HTTP 服务,监听端口 8080
	r.Run(":8080")
}

GET效果
在这里插入图片描述
在这里插入图片描述

POST效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、在中间件中使用Goroutine

package main

import (
	"log"
	"time"

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

func main() {
	// 初始化 Gin 默认实例,它包含了 Logger 和 Recovery 中间件
	r := gin.Default()

	// 异步处理
	r.GET("/long_async", func(c *gin.Context) {
		// 创建在 goroutine 中使用的副本
		// 由于上下文在 Goroutine 中被使用,必须通过 c.Copy() 拷贝一份上下文的副本。
		// 原因是 Gin 的上下文是有状态的,多 Goroutine 并发访问时不能共享原始上下文。
		cCp := c.Copy()

		// 启动 Goroutine 以模拟长任务
		go func() {
			// 用 time.Sleep 模拟一个耗时任务,执行 5 秒。
			time.Sleep(5 * time.Second)

			// 使用副本上下文来访问请求的路径信息
			// 如果直接使用原始上下文,可能会导致并发问题。
			log.Println("Done! in path " + cCp.Request.URL.Path)
		}()
	})

	// 同步处理
	r.GET("/long_sync", func(c *gin.Context) {
		// 同步处理时,没有 Goroutine,所以可以直接使用原始上下文。
		// 用 time.Sleep 模拟一个长任务,执行 5 秒。
		time.Sleep(5 * time.Second)

		// 输出请求路径
		log.Println("Done! in path " + c.Request.URL.Path)
	})

	// 启动服务器,监听在 0.0.0.0:8080
	r.Run(":8080")
}

打开浏览器
访问 http://localhost:8080/long_async 来测试异步处理,浏览器/Postman 会立即返回响应,不会等到 5 秒任务完成。控制台在 5 秒后会输出 Done! in path /long_async
在这里插入图片描述

访问 http://localhost:8080/long_sync 来测试同步处理。浏览器/Postman 会等到 5 秒任务完成后再返回响应。控制台会立即输出 Done! in path /long_sync
在这里插入图片描述

五、自定义中间件

package main

import (
	"log"
	"time"

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

// Logger 自定义中间件,用于记录请求的延迟和状态
func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 记录当前时间,用于计算请求延迟
		t := time.Now()

		// 设置一个名为 "example" 的上下文变量,其值为 "12345"
		c.Set("example", "12345")

		// 请求前的操作可以在这里进行
		// 例如:记录请求开始时间或执行一些检查

		// 处理请求,调用后续的处理函数
		c.Next()

		// 请求后的操作可以在这里进行
		// 计算请求处理的延迟
		latency := time.Since(t)
		// 打印延迟信息到日志
		log.Print(latency)

		// 获取并打印响应状态码
		status := c.Writer.Status()
		log.Println(status)
	}
}

func main() {
	// 创建一个新的 Gin 实例,没有默认中间件
	r := gin.New()

	// 使用自定义的 Logger 中间件
	r.Use(Logger())

	// 定义一个 GET 路由 /test
	r.GET("/test", func(c *gin.Context) {
		// 从上下文中获取 "example" 变量,并进行类型断言
		example := c.MustGet("example").(string)

		// 打印获取到的 "example" 变量值:12345
		log.Println(example)
	})

	// 启动服务器,监听在 0.0.0.0:8080
	r.Run(":8080")
}

效果
在这里插入图片描述


http://www.kler.cn/news/357534.html

相关文章:

  • 计算机网络—vlan(虚拟局域网)
  • 【exceljs】纯前端如何实现Excel导出下载和上传解析?
  • efficientNetV2骨干
  • 搜维尔科技:我们用xsens动作捕捉技术制作的数字人
  • Python基础之集合使用详解
  • 2-127基于matlab的非圆齿轮啮合动画设计
  • 基于Python+Flask的天气预报数据可视化分析系统(源码+文档)
  • Maxwell 底层原理 详解
  • 【Kafka】Kafka Producer的缓冲池机制原理
  • 目标检测数据集图片及标签同步裁剪
  • 下载并安装 WordPress 中文版
  • 数字后端实现静态时序分析STA Timing Signoff之min period violation
  • LeetCode.102 二叉树的层序遍历
  • 【无标题】vertex shader and fragment shader
  • 美摄科技云服务解决方案,方案成熟,接入简单
  • 从零开始搭建图像去雾神经网络(论文复现)
  • 多数元素问题
  • JAVA-石头迷阵小游戏
  • Windows 添加右键以管理员身份运行 PowerShell
  • 关于网络接口监测工具ifstat命令的功能详解以及Linux下lsof命令的使用详解