什么是中间件

中间件,英译middleware,顾名思义,放在中间的物件,那么放在谁中间呢?本来,客户端可以直接请求到服务端接口。

现在,中间件横插一脚,它能在请求到达接口之前拦截请求,做一些特殊处理,比如日志记录,故障处理等。这就是今天要讲述的中间件,那么,它在Gin框架中是怎么使用的呢?

如何使用中间件

我们来看一下逢gin必调的方法Default,方法中有一个变量engine,它UseLoggerRecovery两个函数,这两个函数就是gin框架的日志和故障处理中间件。

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

那就很清楚了,使用中间件就是调用Use方法就行了呗,问题是现在除了这两个中间件还能去Use谁?不如咱先自己写一个中间件吧,这样比较容易理解。

写一个中间件

写啥呢,做产品讲究MVP,那咱就写个最简单的闭环,拦截请求后输出平也最帅的日志,产品就可以交付了。

写之前先研究一下官方的LoggerRecovery是怎么写的,好比葫芦画瓢。

func Logger() HandlerFunc {
return LoggerWithConfig(LoggerConfig{})
} func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
}

原来这两个函数都返回了HandlerFunc类型,那我们也模仿写一个函数就好了。

func PingYe() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(200, "平也最帅")
}
}

很简单,写完了,可以在main函数中Use它了。

func main() {
r := gin.Default()
r.Use(PingYe())
r.Run()
}

把项目跑起来,访问localhsot:8080看一下,帅呆了,成功渲染数据。

是不是太简单了?这就交差了?我还能打十个啊!?

看来我要把毕生所学都交给你了。

延伸阅读

Next

假如我们定义了两个中间件,一个是平也最帅,另一个是在哪里最帅。

func PingYe() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(200, "平也最帅")
}
} func Where() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(200, "在全宇宙")
}
}

按顺序把它们分别注册到框架当中,这个时候我们猜测它会先输出平也最帅再输出在全宇宙对吧?对,确实是的。

func main() {
r := gin.Default()
r.Use(PingYe(), Where())
r.Run()
}

但是,如果我在不更改注册顺序的前提下,怎么调换一下顺序,先输出在全宇宙再输出平也最帅呢?这就用到了大名鼎鼎的Next方法。它的作用就是先执行以下一个中间件,执行完了再回来继续执行接下来的逻辑。记得是在中间件中调用哦~

func PingYe() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
c.String(200, "平也最帅")
}
}

Abort

当然,除了提供Next方法外,理论上也应该有个中断操作吧,毕竟拿中间件来做授权验证的话,验证失败后还是希望阻断请求的。所以,Abort就是我们要找的那个方法。拿上面的例子,在平也最帅的下一行调用Abort方法后,Where中间件就不再生效了,于是平也只剩下了单纯的帅气。

func PingYe() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(200, "平也最帅")
c.Abort()
}
}

局部中间件

刚才我讲的中间件是会在所有的路由上生效的,有些不需要添加中间件的路由场景就无法适应了。所以,我们需要有能为局部添加中间件的能力。

我们先实现给某个接口单独加中间件,所以先得定义两个接口knowunknown,分别代表认识平也与不认识两个场景,鉴于认识后才知道平也是全宇宙最帅的,所以要绑中间件,不认识就算了。实现方式非常简单,往路由后面的参数拼命加中间件就好了。

r.GET("know", PingYe(), Where())
r.GET("unknown", func(c *gin.Context) {
c.String(200, "???")
})

除了针对某个接口添加中间件外,还可以针对一组接口添加,同样调用Use方法即可。

v1 := r.Group("v1")
v1.Use(PingYe(), Where())
{
v1.GET("/know", func(c *gin.Context) {
c.String(200, "know")
})
v1.GET("/unknown", func(c *gin.Context) {
c.String(200, "unknown")
})
}

HTTP基本认证

基本认证,又称BasicAuth,加了基本认证的接口,会让你在访问接口时提供用户名与密码。

对于浏览器用户,为了用户的体验会自动弹出登录框,而在其他场景下是没有的,那在哪里输入账号密码呢?实际上,它是通过头信息传输的,头信息里有一个固定的格式来代表基本认证。

Authorization: Basic <凭证>

凭证部分是是用户名和密码组合的base64编码,两者以冒号方式拼接。然鹅,gin框架的BaseAuth中间件早已准备好了一切,它可以短短几行代码就能解析基本认证的信息。

func main() {
r := gin.Default()
r.Use(gin.BasicAuth(gin.Accounts{
"pingye": "123",
}))
r.GET("/secrets", func(c *gin.Context) {
user := c.MustGet(gin.AuthUserKey).(string)
c.String(200, user+"已登录成功")
})
r.Run()
}

示例中的部分代码可能有些同学不太明白,比如BasicAuth方法中的参数,因为基本认证需要账号和密码对吧,所以我们可以利用gin.Accounts方便的配置好需要验证的账号密码,gin.Accounts是一个map类型,键代表用户名,值代表密码,当然可以设置不止一个键值对,根据你的喜好自行设置。

代码中还出现了c.MustGet方法,这个方法的作用就是一定要取到某个参数,取不到就不干了panic,取的是什么呢?就是gin.AuthUserKey,在官方中的解释是基本认证中用户凭证的cookie名称。

// AuthUserKey is the cookie name for user credential in basic auth.
const AuthUserKey = "user"

Go语言库示例开源项目「golang-examples」欢迎star~

https://github.com/pingyeaa/golang-examples

感谢大家的观看,如果觉得文章对你有所帮助,欢迎关注公众号「平也」,聚焦Go语言与技术原理。

Gin框架系列03:换个姿势理解中间件的更多相关文章

  1. Gin框架系列02:路由与参数

    回顾 上一节我们用Gin框架快速搭建了一个GET请求的接口,今天来学习路由和参数的获取. 请求动词 熟悉RESTful的同学应该知道,RESTful是网络应用程序的一种设计风格和开发方式,每一个URI ...

  2. Gin框架系列01:极速上手

    Gin是什么? Gin是Go语言编写的web框架,具备中间件.崩溃处理.JSON验证.内置渲染等多种功能. 准备工作 本系列演示所有代码都在Github中,感兴趣的同学可以自行查阅,欢迎大家一起完善. ...

  3. [系列] Gin框架 - 数据绑定和验证

    目录 概述 推荐阅读 概述 上篇文章分享了 Gin 框架使用 Logrus 进行日志记录,这篇文章分享 Gin 框架的数据绑定与验证. 有读者咨询我一个问题,如何让框架的运行日志不输出控制台? 解决方 ...

  4. gin框架教程:代码系列demo地址

    gin框架教程代码地址: https://github.com/jiujuan/gin-tutorial demo目录: 01quickstart 02parameter 03route 04midd ...

  5. Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  6. java io系列03之 ByteArrayOutputStream的简介,源码分析和示例(包括OutputStream)

    前面学习ByteArrayInputStream,了解了“输入流”.接下来,我们学习与ByteArrayInputStream相对应的输出流,即ByteArrayOutputStream.本章,我们会 ...

  7. Gin框架 - 自定义错误处理

    目录 概述 错误处理 自定义错误处理 panic 和 recover 推荐阅读 概述 很多读者在后台向我要 Gin 框架实战系列的 Demo 源码,在这里再说明一下,源码我都更新到 GitHub 上, ...

  8. Tk 的基本概念-组件—Tkinter 教程系列03

    Tk 的基本概念-组件-Tkinter 教程系列03 前言 Tk 系列教程: Tkinter教程系列01--引言和安装Tk Tkinter教程系列02--第一个真正的 GUI 程序 通过上一节的程序实 ...

  9. Spring框架系列(13) - SpringMVC实现原理之DispatcherServlet的初始化过程

    前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,包含DispatcherServlet的初始化过程和DispatcherServlet ...

随机推荐

  1. jQuery上传文件按钮美化

    效果图如下: 思路: 1:打开文件设置为透明,外面包一层标签,给标签设置颜色背景,给人点击浏览其实是点击打开文件的错觉.(给外标签相对定位,打开文件标签绝对定位). 2:点击浏览后,选择了文件,就把文 ...

  2. 最简单易懂的实现CRC16校验

    public String getCRC16(byte[] bytes) { //CRC寄存器全为1 int CRC = 0x0000ffff; //多项式校验值 int POLYNOMIAL = 0 ...

  3. nginx前端服务部署

    一.登录服务器 登录跳板机 执行list,列出所有机器 执行dssh 机器序号,如dssh 1,选择机器 二. 创建nginx配置文件 进入nginx配置目录:cd usr/local/nginx/c ...

  4. Cisco asa组建IPSEC for ikev1

    IPSec的实现主要由两个阶段来完成:--第一阶段,双方协商安全连接,建立一个已通过身份鉴别和安全保护的通道.--第二阶段,安全协议用于保护数据的和信息的交换. IPSec有两个安全协议:AH和ESP ...

  5. Sequence to Sequence Learning with Neural Networks论文阅读

    论文下载 作者(三位Google大佬)一开始提出DNN的缺点,DNN不能用于将序列映射到序列.此论文以机器翻译为例,核心模型是长短期记忆神经网络(LSTM),首先通过一个多层的LSTM将输入的语言序列 ...

  6. python基础学习day7

    基础数据类型的补充:编码的进阶 str capitalize() 首字母(第一个单词)大写,其余变小写 s1 = 'I LIVE YOU' print(s1.capitalize()) >> ...

  7. js运算【按位非】~ (index = ~~this.userIndex)(~~ 双破折号 如果是数字返回数字,如果不是数字 返回0)

    index = ~~this.userIndex ~~ 双破折号 如果是数字返回数字,如果不是数字 返回0 这个运算符有点意思:按位非[~] 先来几个例子: ~undefined: -1 ~false ...

  8. Python - 面向对象(二)类方法、静态方法

    面向对象的各种方法 静态方法 - @staticmethod class Person(): name = "cool guy" @staticmethod def static( ...

  9. Error 不再支持源选项 5。请使用 6 或更高版本。

    解决方案:在项目pom.xml中指定JDK版本 我的jdk版本是11.0.2 所以写的是11 根据你自己的jdk版本写 1.7/1.8~~~~ <properties>元素时根元素< ...

  10. Dubbo 扩展点加载机制:从 Java SPI 到 Dubbo SPI

    SPI 全称为 Service Provider Interface,是一种服务发现机制.当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类.所以在程序中并没有直接指定使用接口的哪个实 ...