所有文章 > API开发 > 使用gin搭建api后台系统之中间件开发

使用gin搭建api后台系统之中间件开发

在之前的文章中,基本上都是使用的默认的 gin.Default() 来初始化一个引擎,之后再调用引擎的Run方法来运行一个web服务,现在如果有个需求,某些api只有管理员权限的才可以进行访问,非管理员的就很401,我们可以写一个方法,在需要授权的接口调用该方法来进行验权,但是在gin中还可以使用更加优雅的方法,中间件。

gin.Default() 是如何工作的?

我们先来看下gin.Default() 是如何工作的

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

它先使用New()方法初始化一个引擎,然后再调用引擎的Use 方法,加载了两个「中间件」

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
}

加载了两个gin中的中间件,Logger()与Recovery()

Logger()的源码为

func Logger() HandlerFunc {
return LoggerWithConfig(LoggerConfig{})
}

Recovery() 的源码为

func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
}

什么是中间件

中间件为一类函数,「HandlerFunc」 , 定义为 type HandlerFunc func(*Context)

也就是该函数的参数为Context,也就是说,如果我们要自定义一个中间件函数的话,只需要返回一个参数为(*gin.Content)的函数即可。

何为中间件?当一个请求到达gin服务的某个路由以后,gin 会根据路由中定义好的处理类来进行处理,以GET方法为例

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

handlers 为不定参数,也就是说可以有多个处理类,而这里的处理类类型也是HandlerFunc,和中间件是一样的。

可以理解为是中间件为多个处理请求的Handler,某个请求先经过一个Handler, 之后再经过第二个,第三个,最后将结果返回给调用者。

中间件函数中有以下两个方法比较重要

Next() 方法,该中间件处理以后,交由下一个中间件处理

Abort() 方法,调用完该方法以后,之后的HandlerFunc则不进行处理了

中间件的两种定义方式

中间件常用的有两种方式

  1. 定义一个返回值为gin.HandlerFunc 的函数,
  2. 直接定义一个参数为*gin.Context 的函数
package midwares

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

//打印耗时的中间件

func Checktime() gin.HandlerFunc {
//这里可以处理一些别的逻辑
return func(c *gin.Context) {
start := time.Now()
c.Next()
spend:= time.Since(start).Microseconds()
fmt.Printf("use: %d \n", spend)
}
}

func Abort(c *gin.Context){
fmt.Println("使用了Abort中间件,不会进行之后处理")
c.JSON(200, gin.H{"msg": "no next"})
c.Abort()
}

这两种方式效果是一样的,只是在gin初始化加载的方式不同

r := gin.Default()
r.Use(midwares.Checktime()) //需要加上()
r.Use(midwares.Abort) // 不需要加上()

个人还是比较喜欢加上()的方式。

中间件的几种使用方式

中间件有两种使用方式,一种是全局的中间件,一种是局部中间件

如上面的使用r.Use(midware) 的方式是全局的,这种方式,每个路由都会走该中间件的逻辑。

局部中间件为在路由定义时使用,这种定义则只在该路由上有效.

userv1.GET("/abort", midwares.Abort, userv1_h.Abort)

上面我定义了两个中间件,Checktime 为记录下该请求所用的时间,Abort 中间件为测试c.Abort 函数功能,使用了该中间件的路由只会走到这里,不会再经过之后的路由了。

访问上面的路由地址 /abort 以下,控制台会输出

使用了Abort中间件,不会进行之后处理
use: 443

第一行输出为「Abort」 中间件的输出,第二行输出为「Checktime」中间件的输出。

http的返回为Abort中间件定义的返回,之后的HandlerFunc不再执行了。

{
msg: "no next"
}

基于中间件的授权管理

创建一个中间件,如果校验成功则继续往下走,如果不是管理员,则就不要往下走了,返回401

func CheckAdmin() gin.HandlerFunc{
return func(c *gin.Context) {
username := c.Query("user")
// 这里可以是从cookie或者 session 中判断
if username == "admin"{
c.Next()
}else{
c.JSON(401, gin.H{"msg": "No Private"})
c.Abort()
}
}
}

中间件的执行顺序

gin在初始化的时候加载了一些中间件,又定义了全局的中间件,又定义了路由的中间件,那么这些中间件的执行顺序是如何的呢?

执行的顺序为 全局中间件 > 路由组中间件 > 路由中间件。

参考文章

Gin框架入门(四)—中间件[1]

本文章转载微信公众号@序语程言

#你可能也喜欢这些API文章!