21 go语言(golang) - gin框架安装及使用(二)

四、组成

前面的文章中,我们介绍了其中一部分组成,接下来继续学习:

  1. Router(路由器)

    • Gin 使用基于树结构的路由机制来处理 HTTP 请求。它支持动态路由参数、分组路由以及中间件。
    • 路由器负责将请求路径映射到相应的处理函数。
  2. Context(上下文)

    • gin.Context 是 Gin 中最重要的结构之一,它在请求生命周期内传递信息。
    • Context 提供了对请求和响应对象的访问,以及用于存储数据、设置状态码、返回 JSON 等方法。
  3. Middleware(中间件)

    • 中间件是可以在请求被最终处理之前或之后执行的一段代码,用于实现日志记录、错误恢复、认证等功能。
    • Gin 支持全局中间件和特定路由组或单个路由使用的中间件。
  4. Handlers(处理函数)

    • 处理函数是实际执行业务逻辑的位置,每个路由都会关联一个或多个处理函数。
    • 这些函数接收 gin.Context 参数,通过它们可以获取请求数据并生成响应。
  5. Error Handling(错误处理)

    • Gin 提供了一种机制来捕获和管理应用程序中的错误,可以通过 Context 的方法进行错误报告和恢复操作。
  6. Rendering and Responses(渲染与响应)

    • 支持多种格式的数据输出,包括 JSON、XML 和 HTML 渲染等,方便客户端消费不同类型的数据格式。
  7. Binding and Validation(绑定与验证)

    • 自动将 HTTP 请求中的数据绑定到结构体,并支持对输入数据进行验证,以确保其符合预期格式和规则。
  8. Templates (模板)

    • 虽然不是框架核心,但 Gin 支持集成 HTML 模板引擎,用于生成动态网页内容。

4.3 Middleware(中间件)

4.3.4 中间件的调用顺序

上一章讲的中间件,还有部分内容我们先来看看

4.3.4.1 按注册顺序执行

中间件会按照它们被添加到 Gin 实例上的顺序依次执行

func Test1(t *testing.T) {r := gin.Default()r.Use(func(c *gin.Context) {fmt.Println("func1 ...")})r.Use(func(c *gin.Context) {fmt.Println("func2 ...")})r.GET("/test1", func(c *gin.Context) {fmt.Println("test1 最终路由方法。。。")})r.Use(func(c *gin.Context) {fmt.Println("func3 ...")})r.GET("/test2", func(c *gin.Context) {fmt.Println("test2 最终路由方法。。。")})r.Run()
}

依次调用test1和test2路由,输出

func1 ...
func2 ...
test1 最终路由方法。。。
[GIN] 2024/12/13 - 15:11:44 | 200 |      24.436µs |             ::1 | GET      "/test1"
func1 ...
func2 ...
func3 ...
test2 最终路由方法。。。
[GIN] 2024/12/13 - 15:11:48 | 200 |      21.672µs |             ::1 | GET      "/test2"
4.3.4.2 c.Next()控制顺序

c.Next() 是 Gin 框架中 *gin.Context 类型的方法,用于控制中间件链的执行流程。

  • 当一个中间件调用 c.Next() 时,它将暂停当前处理中函数的执行,并将控制权交给下一个处理中函数或最终的路由处理器。
  • 多个中间件通过调用 c.Next() 可以形成嵌套结构,类似于栈。当所有后续步骤完成后,程序会回到先前上下文继续执行剩余代码。即返回时则是相反顺序(即“先进后出”)。

可以理解为,将原本顺序执行的后续中间件,嵌套在c.Next()中先执行

func Test2(t *testing.T) {r := gin.Default()r.Use(func(c *gin.Context) {fmt.Println("func1 begin...")//c.Next()fmt.Println("func1 end...")})r.Use(func(c *gin.Context) {fmt.Println("func2 begin...")//c.Next()fmt.Println("func2 end...")})r.Use(func(c *gin.Context) {fmt.Println("func3 begin...")//c.Next()fmt.Println("func3 end...")})r.GET("/test_next", func(c *gin.Context) {fmt.Println("test_next 最终路由方法。。。")})r.Run()
}

当注释掉所有c.Next()时,顺序执行

func1 begin...
func1 end...
func2 begin...
func2 end...
func3 begin...
func3 end...
test_next 最终路由方法。。。

打开func1的c.Next()时,后续的中间件和最终路由就全部在c.Next()中执行完,最后才返回func1继续执行,即最后再打印func1 end…

func1 begin...
func2 begin...
func2 end...
func3 begin...
func3 end...
test_next 最终路由方法。。。
func1 end...

再打开func2的c.Next()时,道理相同,c.Next()中嵌套c.Next()

func1 begin...
func2 begin...
func3 begin...
func3 end...
test_next 最终路由方法。。。
func2 end...
func1 end...

再打开func3的c.Next()时

func1 begin...
func2 begin...
func3 begin...
test_next 最终路由方法。。。
func3 end...
func2 end...
func1 end...
4.3.4.3 c.Abort()控制顺序

c.Abort() 是 Gin 框架中 *gin.Context 类型的方法,用于立即停止当前请求的进一步处理。

  • 一旦调用 c.Abort(),当前请求将不再继续传递给下一个处理中函数或最终的路由处理器。
  • 这意味着所有在调用点之后注册的中间件和路由处理器都不会被执行。
  • 调用 c.Abort() 会设置一个内部标志位,指示该请求已经被终止。这个标志可以通过 c.IsAborted() 方法检查。
  • 在某些情况下,例如认证失败、权限不足或其他需要立即返回响应的情况,可以使用 c.Abort() 来阻止进一步操作,并直接返回适当的响应给客户端。
func Test3(t *testing.T) {r := gin.Default()r.Use(func(c *gin.Context) {fmt.Println("func1 begin...")fmt.Println("func1 end...")})r.Use(func(c *gin.Context) {fmt.Println("func2 begin...")fmt.Println(c.IsAborted())c.Abort() // 从此中间件开始,后续的中间件包括最终路由都不再执行fmt.Println(c.IsAborted())fmt.Println("func2 end...") // 但是这行代码会执行})r.Use(func(c *gin.Context) {fmt.Println("func3 begin...")fmt.Println("func3 end...")})r.GET("/test_next", func(c *gin.Context) {fmt.Println("test_next 最终路由方法。。。")c.JSON(200, gin.H{"success": "true",})})r.Run()
}

输出:

func1 begin...
func1 end...
func2 begin...
false
true
func2 end...
4.3.4.3 Next 和 Abort 共同控制?

注:如果Next()中有Abort(),执行顺序是怎样的?

func Test4(t *testing.T) {r := gin.Default()r.Use(func(c *gin.Context) {fmt.Println("func1 begin...")c.Next()fmt.Println("func1 end...")})r.Use(func(c *gin.Context) {fmt.Println("func2 begin...")c.Abort()fmt.Println("func2 end...")})r.Use(func(c *gin.Context) {fmt.Println("func3 begin...")fmt.Println("func3 end...")})r.GET("/test_next", func(c *gin.Context) {fmt.Println("test_next 最终路由方法。。。")c.JSON(200, gin.H{"success": "true",})})r.Run()
}

输出:

func1 begin...
func2 begin...
func2 end...
func1 end...

可以看到,c.Next()还是会执行完,并返回后,打印func1 end...c.Abort()只是控制切断了func3的执行,这里很坑,问了很多AI,回答的都是func1 end...不会执行,要自己试过才知道

4.4 Handlers(处理函数)

处理函数(Handlers)是处理 HTTP 请求的核心组件。它们负责接收请求、执行业务逻辑,并返回响应。

4.4.1 Handlers 的基本概念

  1. HandlerFunc 类型

    • 在 Gin 中,所有的处理函数都必须符合 gin.HandlerFunc 类型。
    • gin.HandlerFunc 被定义为:type HandlerFunc func(*Context)
    • 这意味着任何接受一个指向 *gin.Context 参数且无返回值的函数都可以作为一个合法的 Handler。
  2. Context 对象

    • 每个 Handler 都会接收到一个 *gin.Context 对象,它封装了请求和响应的信息。
    • 开发者可以通过这个对象来获取请求数据(如查询参数、表单数据、JSON 数据等)、设置响应状态码和内容,以及控制请求流转(如调用 Next()Abort())。

4.4.2 使用

我们在前面的代码例子中,无论是绑定在路由上,还是作为中间件使用,都是这个HandlerFunc类型

  1. Handlers 通常与特定路由绑定在一起,通过 HTTP 方法(如 GET, POST)以及路径进行匹配。
  2. 也可以多个 Handlers 可以组成中间件链,每个处理中步骤按顺序执行。
  3. 匿名函数或命名函数皆可用作 Handler
func myHandler(c *gin.Context) {fmt.Println("命名函数")
}func Test5(t *testing.T) {r := gin.Default()// 使用匿名函数my := func(c *gin.Context) {fmt.Println("匿名函数1")}r.GET("/t1", my)// 使用匿名函数r.GET("/t2", func(c *gin.Context) {fmt.Println("匿名函数2")})// 使用命名函数r.GET("/t3", myHandler)r.Run()
}

4.5 Error Handling(错误处理)

错误处理(Error Handling)帮助开发者在请求处理过程中捕获和管理错误。Gin 提供了一些机制来简化错误的记录、传递和响应。

4.5.1 基本概念

Gin 使用 gin.Error 类型来表示错误。它包含了一个 error 接口,以及一些附加信息:元数据(Meta any)和类型标识(Type ErrorType),其中的错误类型用于标识不同种类的错误,例如绑定错误、渲染错误等。可以通过设置不同的类型来对错误进行分类。

// 结构体的源码:
// Error represents a error's specification.
type Error struct {Err  errorType ErrorTypeMeta any
}

4.5.2 错误处理机制

4.5.2.1 Context 中的 Errors 字段
  • 每个 *gin.Context 对象都有一个 Errors 字段,这是一个存储所有发生在该请求生命周期内的 gin.Error 切片。
  • 开发者可以通过这个字段访问并操作这些累积起来的错误,并根据需要进行进一步操作(如日志记录或响应生成)。
4.5.2.2 添加错误

在处理中函数中,可以使用 c.Error(err) 方法将新的 error 添加到当前上下文中。

r.Use(func(c *gin.Context) {fmt.Println("func1...")c.Error(fmt.Errorf("手动写入错误1"))})
4.5.2.3 检索错误

可以通过迭代上下文中的 Errors 来访问所有已记录过得 errors。

func Test6(t *testing.T) {r := gin.Default()r.Use(func(c *gin.Context) {fmt.Println("func1...")c.Error(fmt.Errorf("手动写入错误1"))})r.Use(func(c *gin.Context) {fmt.Println("func2...")c.Error(fmt.Errorf("手动写入错误2"))})r.GET("/error", func(c *gin.Context) {errors := c.Errors// 遍历所有的错误for _, err := range errors {fmt.Println(err)}fmt.Println("正常返回")})r.Run()
}

输出,其中最后两行的日志是gin自带打印的错误信息

func1...
func2...
手动写入错误1
手动写入错误2
正常返回
[GIN] 2024/12/16 - 10:50:56 | 200 |      34.129µs |       127.0.0.1 | GET      "/error"
Error #01: 手动写入错误1
Error #02: 手动写入错误2

4.5.3 自定义全局异常处理中间件

为了统一管理应用程序中的异常情况,通常会创建一个全局异常处理中间件。在这个中间件里,可以遍历每个请求产生过得 errors 并做出相应反应,比如写入日志系统等。

func myErrorHandler(c *gin.Context) {c.Next() // 执行后续处理中步骤errors := c.Errors// 检查是否有任何errors被注册if len(errors) > 0 {for i, e := range errors {fmt.Println(i, e) // 打印或保存日志信息}// 返回通用响应给客户端c.JSON(500, gin.H{"msg": "内部错误",})}
}func Test7(t *testing.T) {r := gin.Default()r.Use(myErrorHandler)r.GET("/error", func(c *gin.Context) {// 模拟业务执行过程中的错误c.Error(fmt.Errorf("手动写入错误"))c.JSON(200, gin.H{"msg": "成功",})})r.Run()
}

客户端输出,可以看到,返回的有点错乱

{"msg": "成功"
}{"msg": "内部错误"
}

所以,要实现类似spring中全局的错误处理,还需要改进一下

func myErrorHandler2(c *gin.Context) {defer func() {errors := c.Errors// 在golang中,这些错误属于可以预期的错误,可以简单的打印日志或做对应的业务处理,真正类似java中不可预期的是panicif len(errors) > 0 {for i, e := range errors {fmt.Println(i, e) // 打印或保存日志信息}}// 这里处理真正的不可预期的报错hasPanic := recover()if hasPanic != nil {fmt.Println("捕获到异常!!")c.Abort() // 这行代码一定要加,不然后续代码还会执行,比如在最终路由中的打印// 返回通用响应给客户端c.JSON(500, gin.H{"msg": "内部错误",})}}()c.Next() // 执行后续处理中步骤
}func Test8(t *testing.T) {r := gin.Default()r.Use(myErrorHandler2)r.Use(func(c *gin.Context) {// 模拟错误list := []int{1, 2, 3, 4}i := list[5]println(i)})r.GET("/error", func(c *gin.Context) {fmt.Println("正常进入最终路由")c.JSON(200, gin.H{"msg": "成功",})})r.Run()
}

4.5.4 AbortWithError

Gin 提供了便捷方法 AbortWithError(statusCode int , err error ) ,用于同时终止请求并将指定状态码与error对象一起加入到context.errors列表内 。这使得开发者能够快速地结合HTTP协议标准状态码与具体业务逻辑需求来构建更具表达力且一致性强大的API接口 。

func Test9(t *testing.T) {r := gin.Default()r.Use(func(c *gin.Context) {fmt.Println("func1...")})r.Use(func(c *gin.Context) {random := rand.Intn(2)// 模拟随机错误if random == 0 {// 将错误添加到Context中,并且终止后续的调用链c.AbortWithError(500, fmt.Errorf("内部错误"))// 等价于//c.Error(fmt.Errorf("内部错误"))//c.AbortWithStatus(500)return}fmt.Println("func2...")})r.Use(func(c *gin.Context) {fmt.Println("func3...")})r.GET("/error", func(c *gin.Context) {c.JSON(200, "正常")})r.Run()
}

输出

// 异常情况
func1...
[GIN] 2024/12/16 - 16:34:28 | 500 |      18.752µs |       127.0.0.1 | GET      "/error"
Error #01: 内部错误// 正常情况
func1...
func2...
func3...
[GIN] 2024/12/16 - 16:34:30 | 200 |      61.868µs |       127.0.0.1 | GET      "/error"

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

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

相关文章

Fastdfs V6.12.1集群部署(arm/x86均可用)

文章目录 一、 Fastdfs 介绍二、部署 信息三、步骤tracker/storage 机器的 compose 内容storage 机器的 composetracker 与 storage 启动目录层级与配置文件测试测试集群扩容与缩减注意事项 一、 Fastdfs 介绍 FastDFS 是一款高性能的分布式文件系统,特别适合用于存…

零基础开始学习鸿蒙开发-基础页面的设计

目录 1.样例图 2.逐项分析 2.1 头顶布局分析:首先我们要把第一行的图标绘制出来,一个左一个右,很明显,需要放在一个Row容器中,具体代码如下: 2.2 和头像同一行的布局,需要注意的是&#xff0c…

如何用细节提升用户体验?

前端给用户反馈是提升用户体验的重要部分,根据场景选择不同的方式可以有效地提升产品的易用性和用户满意度。以下是常见的方法: 1. 视觉反馈 用户执行了某些操作后,需要即时确认操作结果。例如:按钮点击、数据提交、页面加载等。…

[数据结构#2] 图(1) | 概念 | 邻接矩阵 | 邻接表 | 模拟

图是由顶点集合及顶点间的关系(边)组成的数据结构,可用 G ( V , E ) G(V,E) G(V,E)表示,其中: 顶点集合 V V V: V { x ∣ x ∈ 某数据对象集 } V\{x|x\in\text{某数据对象集}\} V{x∣x∈某数据对象集},…

rpc设计的再次思考20251215(以xdb为核心构建游戏框架)

1.服务提供者注册的方式 // 表明这是一个服务提供者,ServerType 和 ServerId从application.properties中读取 // 而且只有当当前服务是Game时,才生效。 或者 条件注解??? RpcProvider(typeServerType.Game) public class GameProvider{MsgReceiver…

学习maven(maven 项目模块化,继承,聚合)

前言 本篇博客的核心:理解maven 项目模块化,继承,聚合 的含义 maven 项目模块化 含义 maven项目模块化:使用maven 构建项目,管理项目的方式,我们可以将maven项目根据内在的关系拆分成很多个小项目【模块】…

【OJ题解】最长回文子串

个人主页: 起名字真南的CSDN博客 个人专栏: 【数据结构初阶】 📘 基础数据结构【C语言】 💻 C语言编程技巧【C】 🚀 进阶C【OJ题解】 📝 题解精讲 目录 **题目链接****解题思路****1. 初步判断****2. 回文子串性质****3. 判断是…

EMQX 可观测性最佳实践

EMQX 介绍 EMQX 是一款开源、高度可伸缩、高可用的分布式 MQTT 消息服务器,同时也支持 CoAP/LwM2M 等一站式 IoT 协议接入。以下是 EMQX 的一些主要特点和功能: 海量连接与高并发:EMQX 能够处理千万级别的并发客户端,支持大规模…

kubeadm_k8s_v1.31高可用部署教程

kubeadm_k8s_v1.31高可用部署教程 实验环境部署拓扑图**署架构方案****Load Balance****Control plane node****Worker node****资源分配(8台虚拟机)**集群列表 前置准备关闭swap开启ipv4转发更多设置 1、Verify the MAC address and product_uuid are u…

mysql flink cdc 实时数据抓取

背景 通过监控mysql日志,获取表字段更新,用来做实时展示。 使用技术:Flink CDC Flink CDC 基于数据库日志的 Change Data Caputre 技术,实现了全量和增量的一体化读取能力,并借助 Flink 优秀的管道能力和丰富的上下游…

element plus el-select修改后缀图标

<el-selectv-model"value"placeholder"请选择工点"size"large":teleported"false":suffix-icon"CaretBottom"style"width: 100px"><el-optionv-for"item in options":key"item.value&quo…

自动驾驶控制与规划——Project 2: 车辆横向控制

目录 零、任务介绍一、环境配置二、算法三、代码实现四、效果展示 零、任务介绍 补全src/ros-bridge/carla_shenlan_projects/carla_shenlan_stanley_pid_controller/src/stanley_controller.cpp中的TODO部分。 一、环境配置 上一次作业中没有配置docker使用gpu&#xff0c;…

Qt6开发自签名证书的https代理服务器

目标&#xff1a;制作一个具备类似Fiddler、Burpsuit、Wireshark的https协议代理抓包功能&#xff0c;但是集成到自己的app内&#xff0c;这样无需修改系统代理设置&#xff0c;使用QWebengineview通过自建的代理服务器&#xff0c;即可实现https包的实时监测、注入等自定义功能…

鸿蒙Next合理使用状态管理总结

在使用鸿蒙Next进行开发时&#xff0c;合理的状态管理对于优化UI性能和提升用户体验至关重要。许多开发者由于对状态管理特性了解不足&#xff0c;常遇到UI不刷新或刷新性能差的问题。本文将从合理使用属性、合理使用ForEach/LazyForEach等方面进行总结&#xff0c;帮助开发者掌…

Windows如何安装Php 7.4

一、进入官网&#xff0c;选择其他版本 https://windows.php.net/download/ 二、配置环境变量 将解压后的php 路径在系统环境变量中配置一下 cmd 后输入 php-v

ensp 静态路由配置

A公司有广州总部、重庆分部和深圳分部3个办公地点&#xff0c;各分部与总部之间使用路由器互联。广州、重庆、深圳的路由器分别为R1、R2、R3&#xff0c;为路由器配置静态路由&#xff0c;使所有计算机能够互相访问&#xff0c;实训拓扑图如图所示 绘制拓扑图 给pc机配置ip地址…

红米Note 9 Pro5G刷LineageOS

LineageOS介绍 LineageOS 是一个基于 Android 的开源操作系统&#xff0c;是面向智能手机和平板电脑等设备的替代性操作系统。它是 CyanogenMod 的继承者&#xff0c;而 CyanogenMod 是曾经非常受欢迎的一个第三方 Android 定制 ROM。 在 2016 年&#xff0c;CyanogenMod 项目因…

ECharts实现数据可视化入门详解

文章目录 ECharts实现数据可视化入门详解一、引言二、基础配置1.1、代码示例 三、动态数据与交互2.1、代码示例 四、高级用法1、多图表组合1.1、在同一容器中绘制多个图表1.2、创建多个容器并分别初始化 ECharts 实例1.3、实现多图联动 五、总结 ECharts实现数据可视化入门详解…

盲盒3.0版h5版-可打包app-新优化版紫色版

整体界面ui美观大气&#xff0c;盲盒项目也是一直比较热门的&#xff0c;各大平台一直自己也有做。 感兴趣的小伙伴可以搭建做自己的项目。盲盒项目的利润率还是很大的。

MacbookPro M1 安装Hive

前提注意⚠️⚠️⚠️ 1&#xff09;在安装Hive前确实需要安装MySQL&#xff0c;因为Hive可以使用MySQL作为元数据存储 2&#xff09;在安装Hive之前&#xff0c;需要先安装Hadoop。Hive是一个构建在Hadoop之上的数据仓库软件&#xff0c;它使用Hadoop的HDFS&#xff08;分布…