8

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

 2 years ago
source link: https://www.yangyanxing.com/article/use-go-gin-midware.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在之前的文章中,基本上都是使用的默认的 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框架入门(四)—中间件


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK