Golang gin middleware的编写与使用 context.Next函数

news2025/2/8 12:15:25

中间件


在web应用服务中,完整的一个业务处理在技术上包含客户端操作、服务器端处理、返回处理结果给客户端三个步骤。

在实际的业务开发和处理中,会有更负责的业务和需求场景。一个完整的系统可能要包含鉴权认证、权限管理、安全检查、日志记录等多维度的系统支持。

鉴权认证、权限管理、安全检查、日志记录等这些保障和支持系统业务属于全系统的业务,和具体的系统业务没有关联,对于系统中的所有业务都适用。

由此,在业务开发过程中,为了更好的梳理系统架构,可以将上述描述所涉及的一些通用业务单独抽离并进行开发,然后以插件化的形式进行对接。这种方式既保证了系统功能的完整,同时又有效的将具体业务和系统功能进行解耦,并且,还可以达到灵活配置的目的。

这种通用业务独立开发并灵活配置使用的组件,一般称之为"中间件",因为其位于服务器和实际业务处理程序之间。其含义就是相当于在请求和具体的业务逻辑处理之间增加某些操作,这种以额外添加的方式不会影响编码效率,也不会侵入到框架中。中间件的位置和角色示意图如下图所示:

比如在浏览电商网站进行购买的时候要进行下单或者购买一些商品,那么需要进行登入,在对登入这个功能而言,那么很多模块都会使用到,都会使用到登入的权限。所以可以将登入的功能做成统一的模块,在需要的地方配置登入的权限,调用这个登入的功能。这样就不需要在下单购买商品的时候还需要去判断是不是已经登入了或者有没有权限。这样可以达到功能的细分,方便开发人员聚焦具体的业务逻辑,这是解耦的一种开发方式。 

还是下单的例子,如果最后一步是下单,那么中间有很多操作,权限操作,认证的管理等等都可以放在中间件这个地方。这样权限的认证就不需要在下单这一块写了。

那么中间件写的这一部分不仅仅下单的这个模块可以调用,其他的模块也可以调用。这就是中间件的一个作用,最后程序结束了原路的返回就行了。

由此可见中间件的作用是非常明显的。

Gin的中间件


在gin中,中间件称之为middieware,中间件的类型定义如下所示:

// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)

HandlerFunc是一个函数类型,接收一个Context参数。用于编写程序处理函数并返回HandleFunc类型,作为中间件的定义。

type HandlerFunc func(*Context) 其实就代表着是一个中间件。 

 

中间件Use用法


在之前学习的课程中,均使用gin.Default创建了gin引擎engins变量,其中,就使用了中间件。如下图所示:

这里可以看到设置了两个默认的中间件,一个是日志,一个是修复,当程序发生错误的时候会返回500错误,同时将程序发生Panic的时候恢复回来。这样可以让请求比较友好一些,帮助排查服务器内部的错误。

在Default函数中,engine调用Use方法设置了Logger中间件covery中间件。Use函数接收一个可变参数,类型为HandlerFunc,恰为中间件的类型。Use方法定义如下:

这个use方法接受的是可变参数有三个点,可变参数存放的类型是HandlerFunc。这样可以按照自己的自定义需求来添加任意多个中间件。

 

自定义中间件


根据上文的介绍,可以自己定义实现一个特殊需求的中间件,中间件的类型是函数,有两条标准:

  • func函数
  • 返回值类型为HandlerFunc

比如,我们自定义一个自己的中间件。在前面所学的内容中,我们在处理请求时,为了方便代码调试,通常都将请求的一些信息打印出来。有了中间件以后,为了避免代码多次重复编写,使用统一的中间件来完成。定义一个名为RequestInfos的中间件,在该中间件中打印请求的path和method。具体代码实现如下所示:

package main

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

func RequestInfo() gin.HandlerFunc {
	return func(context *gin.Context) {
		path := context.FullPath()
		method := context.Request.Method
		fmt.Println("请求路径为:", path, "请求方法:", method)
	}
}

func main() {
	engine := gin.Default()
	engine.Use(RequestInfo())
	engine.GET("/query", func(c *gin.Context) {
		c.JSON(http.StatusOK, map[string]interface{}{
			"code": 1,
			"msg":  c.FullPath(),
		})
	})
	engine.Run()
}

请求路径为: /query 请求方法: GET
[GIN] 2023/06/14 - 15:54:38 | 200 |     114.125µs |       127.0.0.1 | GET      "/query"

如果有多个请求,想单独的为某个接口解析来使用中间件的话,那么可以在接口解析的方法当中对其单独的使用,使用中间件作为参数传递进去。

func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
第二个是可变一类的参数
	engine.GET("/get", RequestInfo(), func(context *gin.Context) {

	})

上面可以看到中间件在打印的调试信息都是在请求之前打印的,都是在接口解析之前。当某个接口解析处理之后的信息也通过中间件打印出来。

 

 

 

context.Next函数


通过一个例子和使用场景来说明Next函数的作用。

在上文自定义的中间件RequestInfos中,打印了请求了请求的path和method,接着去执行了正常的业务处理函数。如果我们想输出业务处理结果的信息,该如何实现呢。答案是使用context.Next函数。

context.Next函数可以将中间件代码的执行顺序一分为二,Next函数调用之前的代码在请求处理之前之前,当程序执行到context.Next时,会中断向下执行,转而先去执行具体的业务逻辑,执行完业务逻辑处理函数之后,程序会再次回到context.Next处,继续执行中间件后续的代码。

具体用法如下:

package main

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

func RequestInfo() gin.HandlerFunc {
	return func(context *gin.Context) {
		path := context.FullPath()
		method := context.Request.Method
		fmt.Println("请求路径为:", path, "请求方法:", method)
		fmt.Println("状态码信息:", context.Writer.Status())

		context.Next() //这样会将func代码5行一分为二,第一部分是前面三行,执行完第一部分之后到了next
		//它就不再往下执行了,那么就会走下一个中间件的解析

		fmt.Println("状态码信息:", context.Writer.Status())
	}
}

func main() {
	engine := gin.Default()
	engine.Use(RequestInfo())
	engine.GET("/query", func(c *gin.Context) {
		c.JSON(404, map[string]interface{}{
			"code": 1,
			"msg":  c.FullPath(),
		})
		fmt.Println("handler func 业务代码")
	})
	engine.Run()
}


请求路径为: /query 请求方法: GET
状态码信息: 200
handler func 业务代码
状态码信息: 404
[GIN] 2023/06/14 - 16:37:42 | 404 |      61.458µs |       127.0.0.1 | GET      "/query"

可以看到next方法将中间件的执行分为了两个部分,这样可以方便处理后续的业务逻辑。

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

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

相关文章

【软件分析/静态分析】chapter3 课程03/04 数据流分析的应用(Data Flow Analysis)

🔗 课程链接:李樾老师和谭天老师的:南京大学《软件分析》课程03(Data Flow Analysis I)_哔哩哔哩_bilibili 南京大学《软件分析》课程04(Data Flow Analysis II)_哔哩哔哩_bilibili 这篇文章总结…

识别一切模型RAM(Recognize Anything Model)及其前身 Tag2Text 论文解读

img 总览 大家好,我是卷了又没卷,薛定谔的卷的AI算法工程师「陈城南」~ 担任某大厂的算法工程师,带来最新的前沿AI知识和工具,欢迎大家交流~ 继MetaAI 的 SAM后,OPPO 研究院发布识别一切模型(Recognize Any…

MySQL如何保证数据的可靠性(保证数据不丢失)

1. 结论: 只要redo log 和 binlog 保证持久化到磁盘,就能确保MySQL异常重启后,数据可以恢复。 2. 机制 WAL机制,(Write Ahead Log): 事务先写入日志,后持久化到磁盘。 3. binlog…

华为OD机试真题 JavaScript 实现【非严格递增连续数字序列】【2022Q4 100分】

一、题目描述 输入一个字符串仅包含大小写字母和数字,求字符串中包含的最长的非严格递增连续数字序列的长度,比如122889属于非严格递增连续数字序列。 二、输入描述 输入一个字符串仅包含大小写字母和数字,输入的字符串最大不超过255个字符…

合金氢化物动力学与瞬时流量计算

在经典的合金氢化物动力学描述中,有一种是用JMAK方程来描述和拟合合金的吸放氢过程,方程很简洁:,其中是反应程度或者百分比,表示合金氢化物吸氢或者放氢的程度,是该合金吸氢或放氢的一种特征常数&#xff0…

57、基于51单片机智能硬币分拣分类机电子存钱罐报警系统设计(程序+原理图+PCB源文件+Proteus仿真+参考论文+参考PPT+元器件清单等)

摘 要 近年来,随着我国经济的发展和社会的进步,邮政事业得到了空前发展。邮政通信网的技术含量不断增加,技术装备水平也在不断的提高,邮件处理已基本实现机械化,并且朝着自动化的方向迈进。本文着眼于我国当前邮政事…

Unity编辑器扩展-第一集-在菜单栏加入自己的按钮

一、概述 unity自己本身就是一个大的程序,我们看见的所有功能,都是用程序写出来的,但是根据各行各业不同的需求,有些时候我们制作时,想要自己编辑一些原有的功能。 二、本节目标效果展示 1.在菜单栏加入属于自己的一…

【前端基础篇】CSS选择器 和 CSS属性

前言:CSS 简介 CSS 概述 CSS ( Cascading Style Sheet ) 层叠样式表,用来修饰 HTML,使得效果更加多样化CSS 在 HTML4.0 中引入,一般在开发过程中,会使用单独的 CSS 文件进行开发,然后将这个独立 CSS 文件引…

Unity编辑器扩展-第二集-按钮排序/分组/放入右键菜单

第一集链接:Unity编辑器扩展-第一集-在菜单栏加入自己的按钮_菌菌巧乐兹的博客-CSDN博客 一、本节目标效果展示 1.按钮排序 变成 2.按钮分组 仔细看,有个灰色的杠杠 3.放入右键菜单 4.皮一下 二、按钮排序具体流程 第一集讲,如果想放入…

Java自定义泛型类、泛型接口、泛型方法以及 泛型擦除的细节

体会&#xff1a;使用泛型的主要优点是能够在编译时而不是在运行时检测错误。 /*** 自定义泛型类*/ public class Order<T> {String orderName;int orderId;//类的内部结构就可以使用类的泛型T orderT;public Order(){//编译不通过 // T[] arr new T[10];//编译…

Unity基础3——Resources资源动态加载

一、特殊文件夹 &#xff08;一&#xff09;工程路径获取 // 注意 该方式 获取到的路径 一般情况下 只在 编辑模式下使用 // 我们不会在实际发布游戏后 还使用该路径 // 游戏发布过后 该路径就不存在了 print(Application.dataPath);&#xff08;二&#xff09;Resources 资…

如何自动生成正交法测试用例?

目录 引言 正交法实验 自动生成正交用例 引言 正交法测试用例是一种高效且可靠的方法&#xff0c;能够最大限度地减少测试工作量&#xff0c;同时保证覆盖所有可能的组合情况。通过了解如何优化这些测试用例的生成过程&#xff0c;可以提高产品的质量&#xff0c;降低故障率…

机器人视觉梳理(上)

原创 | 文BFT机器人 01 机器人视觉的概念 在智能制造过程中&#xff0c;通过传统的编程来执行某一特定动作的机器人&#xff08;机械手、机械手臂、机械臂等&#xff0c;未作特殊说明时&#xff0c;不作严格区分&#xff0c;统一称为机器人&#xff09;&#xff0c;将难以满足制…

【CV大模型SAM(Segment-Anything)】如何保存分割后的对象mask?并提取mask对应的图片区域?

上一篇文章【CV大模型SAM&#xff08;Segment-Anything&#xff09;】真是太强大了&#xff0c;分割一切的SAM大模型使用方法:可通过不同的提示得到想要的分割目标中详细介绍了大模型SAM&#xff08;Segment-Anything&#xff09;的不同使用方法&#xff0c;后面有很多小伙伴给…

【JVM 监控工具】使用JConsole监控进程、线程、内存、cpu、类情况

文章目录 前言一、如何启动JConsole二、如何设置JAVA程序运行时可以被JConsolse连接分析三、JConsole如何连接远程机器的JAVA程序&#xff08;举例说明&#xff09;四、性能分析概述内存线程类VM摘要MBean 五、使用Jconsole监控某方法的性能总结 前言 Jconsole是JDK自带的监控…

网络工程师必备,华为/思科/华三交换机命令对比

本期对华为/思科/华三交换机配置命令对比,来看下他们的区别在哪里&#xff0c;这里对命令做了个汇总&#xff0c;平时对命令不熟的朋友这次可以看下。 另外我们来看下华为与H3C的命令情况&#xff0c;方便大家进行对比。 一、华为交换机基础配置命令 1、创建vlan&#xff1a; …

Unity基础1——3D数学

一、Mathf &#xff08;一&#xff09;Mathf 和 Math ​ Math 是 C# 中封装好的用于数学计算的工具类 —— 位于 System 命名空间中 ​ Mathf 是 Unity 中封装好的用于数学计算的工具结构体 —— 位于 UnityEngine 命名空间中 ​ 他们都是提供来用于进行数学相关计算的 ​…

机器学习 day18(用Tensorflow搭建一个神经网络)

之前搭建神经网络的方法 先初始化输入数据X&#xff0c;创建layer 1并计算激活值a1&#xff0c;创建layer 2并计算激活值a2&#xff0c;这是前向传播代码的显式形式。 另一种简单些的创建神经网络的方法 创建layer 1和layer 2与前一种方法相同&#xff0c;但我们不需要手动…

JUC并发编程学习笔记

1&#xff1a;回顾多线程 进程和线程是什么 进程是操作系统分配资源的最小单元&#xff0c;而线程是cpu调度的最小单元。 java默认有几个线程 2个,main线程和GC线程&#xff08;GC垃圾回收机制&#xff09; java可以开启线程么 不能 并发和并行 并发,多线程操作同一个资源,cp…

纯电驱动车辆动力总成的优化与比较研究

摘要&#xff1a; 不同动力总成拓扑结构的对比分析 前言 纯电驱动的电动汽车因为集成有大容量电池组&#xff0c;可以存储取自公共电网的电能&#xff0c;用来驱动车辆的行驶。相比于传统的混合动力汽车&#xff0c;具有更加优越的节能减排效果和潜力。因此&#xff0c;近年来…