中间件
在web应用服务中,完整的一个业务处理在技术上包含客户端操作、服务器端处理、返回处理结果给客户端三个步骤。
在实际的业务开发和处理中,会有更负责的业务和需求场景。一个完整的系统可能要包含鉴权认证、权限管理、安全检查、日志记录等多维度的系统支持。
鉴权认证、权限管理、安全检查、日志记录等这些保障和支持系统业务属于全系统的业务,和具体的系统业务没有关联,对于系统中的所有业务都适用。
由此,在业务开发过程中,为了更好的梳理系统架构,可以将上述描述所涉及的一些通用业务单独抽离并进行开发,然后以插件化的形式进行对接。这种方式既保证了系统功能的完整,同时又有效的将具体业务和系统功能进行解耦,并且,还可以达到灵活配置的目的。
这种通用业务独立开发并灵活配置使用的组件,一般称之为"中间件",因为其位于服务器和实际业务处理程序之间。其含义就是相当于在请求和具体的业务逻辑处理之间增加某些操作,这种以额外添加的方式不会影响编码效率,也不会侵入到框架中。中间件的位置和角色示意图如下图所示:
Gin的中间件
定义:
// HandlerFunc defines(定义) the handler(处理器) used by gin middleware(中间件) as return value.
// HandleFunc函数 定义了一个返回值可以被用作gin中间件的处理器。
type HandlerFunc func(*Context)
从一开始创建Gin engine使用的函数gin.Default()
源码如下:
// Default returns an Engine instance(实例) with the Logger and Recovery middleware(中间件) already attached(附加).
// Default函数 返回一个已经附加了Logger和Recovery的中间件引擎实例
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
其中第5行enging.Use(Logger(), Recovery())
就是一个使用中间件的实例。
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes
:对引擎中所有的请求,附加中间件。- 参数
middleware ...HandlerFunc
:需要被添加的多个中间件
// Use attaches(附加、附属品) a global(总体的,全局的) middleware to the router.
//ie. the middleware attached though Use() will be included in the handlers chain(链) for every single(单一的) request.
//【中间件将通过Use()被附加在每一个处理器请求中。】
//Even(即使、甚至) 404, 405, static files...
//For example, this is the right(正确的) place for a logger or error management(管理) middleware.
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
}
Logger()
返回的是一个日志的中间件,它可以在控制台输出调试日志。
// Logger instances(实例) a Logger middleware that will write the logs to gin.DefaultWriter.
// By default gin.DefaultWriter = os.Stdout.
func Logger() HandlerFunc {
return LoggerWithConfig(LoggerConfig{})
}
Recovery()
返回的是一个用于捕获任何panic
的中间件,并在捕获之后,如果只有一个panic
的情况下,其向客户端返回一个500错误。
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
}
如何自定义一个中间件
根据上文实例,中间件有两个要点:
1. 中间件是一个func
2. 该func返回值的类型是HandleFunc
例:
方法一:使用 Use() 函数
该方法适用于为一整个engine中所有的请求添加中间件。
中间件调用函数
func main(){
engine := gin.Default()
engine.POST("/yyy",funcfunc(context *gin.Context){
/*TODO 业务逻辑*/
})//这个yyy的POST请求没有绑定中间件
//第一种添加中间件的方法(为以下所有方法添加中间件)
engine.Use(requestInfo())//从这行代码以下定义的请求才会绑定requestInfo()中间件
engine.Get("/xxx", funcfunc(context *gin.Context){
/*TODO 业务逻辑*/
})//该xxx的GET请求已绑定了request中间件。
engine.Run()
}
中间件函数
//用于向控制台输出请求信息的中间件
func requestInfo() gin.HandlerFunc {
return func(context *gin.Context) {
//执行具体业务之前运行
fmt.Println("(middleware)request route: ", context.FullPath(), " request method: ", context.Request.Method)
fmt.Println("(middleware)wrong status code:", context.Writer.Status()) //这里获取到的状态码应该是错误的(永远都是200)
//开始执行具体业务
context.Next()
//执行完具体业务之后
fmt.Println("(middleware)right status code:", context.Writer.Status())
}
}
方法二:单个Handle使用中间件的方式
该方法适用于对某一个Handle处理器使用特定的中间件
处理器函数
engine.GET("/M4SH", requestInfo(), func(context *gin.Context) {
fmt.Println("↓---运行具体业务---↓")
context.JSON(404, map[string]interface{}{
"code": 0,
"msg": "咕噜灵波",
})
fmt.Println("↑---运行具体业务---↑")
})
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes
:第一种方法Use()
函数,只为在该语句后面的处理器函数附加中间件。其使用了可变参数,意味着该函数可以同时添加多个中间件。func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
:第二种方法,在定义处理器的同时,直接附加该处理器所需要的中间件。该函数也可以传入可变参数。一般情况下,会把真正处理业务逻辑的匿名函数放在最后一个参数位置上。func (c *Context) Next()
:context.Next()
只允许使用在中间件函数中。该函数可以把一个中间件函数分成三层。
有些中间件功能必须在第三层后续语句中运行,例如:
fmt.Println("(middleware)wrong status code:", context.Writer.Status()) //这里获取到的状态码是错误的(是200)
context.Next() //开始执行具体业务,例如具体业务返回了404错误码。
//执行完具体业务之后
fmt.Println("(middleware)right status code:", context.Writer.Status())//获取到的状态码是404,正确