使用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则不进行处理了
中间件的两种定义方式
中间件常用的有两种方式
定义一个返回值为gin.HandlerFunc 的函数,
直接定义一个参数为*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在初始化的时候加载了一些是间件,又定义了全局的中单件,又定义了路由的中间件,那么这些中间件的执行顺序是如何的呢?
执行的顺序为 全局中间件 > 路由组中间件 > 路由中间件。