什么是中间件
中间件(middleware)是一种独立的组件或模块,通常是一个函数或一组函数,常被用于处理请求或事件的过程中
中间件常用来实现一些通用的功能,如日志记录、权限校验、数据加工等
简单理解,中间件就是一面墙,立在函数的前面,每次调用这个函数前都要经过中间件。最容易想到的应用场景就是权限校验,用户不登陆就无法发布文章等等
Gin中的中间件
Gin框架中的中间件是一个gin.HandlerFunc类型,可以为每个路由添加任意数量的中间件
// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
在源码中可以看到后面的参数为...HandlerFunc,是一个可变长度的HandlerFunc类型
配置路由的时候在回调函数前加上一个HandlerFunc类型的函数,就完成了中间件的注册
// 简单的中间件实现
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func test(c *gin.Context) {
fmt.Println("test")
c.JSON(200, gin.H{
"message": "Test",
})
}
func m1(c *gin.Context) {
fmt.Println("m1......")
}
func main() {
router := gin.Default()
router.GET("/test", m1, test)
router.Run()
}
// m1......
// test
访问localhost:8080后,控制台会依次打印
m1......
test
在执行路由处理函数test之前,先经过了m1中间件,这就是中间件最简单的使用
除了给路由单独注册中间件,也可以使用use()函数全局注册
// Use attaches a global middleware to the router. i.e. the middleware attached through Use() will be
// included in the handlers chain for every single request. 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
}
// 简单的全局注册中间件
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func test(c *gin.Context) {
fmt.Println("test")
c.JSON(200, gin.H{
"message": "Test",
})
}
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(200, gin.H{
"message": "Index",
})
}
func m1(c *gin.Context) {
fmt.Println("m1......")
}
func main() {
router := gin.Default()
// 全局注册m1中间件
router.Use(m1)
router.GET("/test", test)
router.GET("/index", index)
router.Run()
}
router.use()就可以实现全局注册,每一个路由都注册了此中间件
还可以给路由组注册
// 写法1
testGroup := router.Group("/test", m1)
{
testGroup.GET("/test1", func(c *gin.Context) {...})
...
}
// 写法2
testGroup := router.Group("/test")
testGroup.Use(m1)
{
testGroup.GET("/test1", func(c *gin.Context) {...})
...
}
流程控制
单纯的打印文字到控制台上没有意义,可以结合流程控制语句实现一些功能
比如写一个Cors跨域中间件,这里只是举一个中间件的使用例子,不再介绍关于跨域相关内容,感兴趣可以查看:跨源资源共享(CORS) - HTTP | MDN (mozilla.org)
// Cors 跨域处理
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
// 允许所有的域名进行跨域请求
c.Header("Access-Control-Allow-Origin", "*")
// 允许跨域的请求头
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
// 允许跨域请求的方法
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
// 允许跨域请求的响应头
c.Header("Access-Control-Expose-Headers",
"Content-Length, Access-Control-Allow-Origin, "+
"Access-Control-Allow-Headers, Content-Type")
// 允许跨域请求携带cookie
c.Header("Access-Control-Allow-Credentials", "true")
method := c.Request.Method
// OPTIONS方法用于获取目的资源所支持的通信选项
if method == "OPTIONS" {
// 如果是OPTIONS请求,直接返回空响应
c.AbortWithStatus(http.StatusNoContent)
}
// 处理请求
c.Next()
}
}
在gin框架中,中间件流程控制函数包括:
1. c.Next(): 调用该函数会将控制权交给下一个中间件函数,如果没有下一个中间件函数,则将控制权交给处理请求的路由处理函数
2. c.Abort(): 调用该函数会立即终止当前中间件函数的执行,并且不会再调用后续的中间件函数或路由处理函数
3. c.AbortWithStatus(code int): 调用该函数会终止当前中间件函数的执行,并返回指定的HTTP状态码给客户端
4. c.NextWithError(): 调用该函数会将控制权交给下一个中间件函数,同时传递一个错误给下一个中间件函数或路由处理函数
5. c.IsAborted(): 该函数用于判断当前请求是否已经被终止,返回一个布尔值表示请求是否已经被终止
看一个简单的使用流程控制的例子
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func test(c *gin.Context) {
fmt.Println("test")
c.JSON(200, gin.H{
"message": "Test",
})
}
func m1(c *gin.Context) {
fmt.Println("m1 begin")
c.Next()
fmt.Println("m1 end")
}
func m2(c *gin.Context) {
fmt.Println("m2 begin")
c.Next()
fmt.Println("m2 end")
}
func main() {
router := gin.Default()
router.GET("/test", m1, m2, test)
router.Run()
}
// m1 begin
// m2 begin
// test
// m2 end
// m1 end
c.Next()的作用是先调用下一个函数,下一个函数执行完之后再回头执行中间件后面的语句
整个过程就像洋葱一样
内置中间件
Gin框架也内置一些中间件,可以直接使用
func BasicAuth(accounts Accounts) HandlerFunc
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc
func Bind(val interface{}) HandlerFunc
func ErrorLogger() HandlerFunc
func ErrorLoggerT(typ ErrorType) HandlerFunc
func Logger() HandlerFunc
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
func LoggerWithFormatter(f LogFormatter) HandlerFunc
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
func Recovery() HandlerFunc
func RecoveryWithWriter(out io.Writer) HandlerFunc
func WrapF(f http.HandlerFunc) HandlerFunc
func WrapH(h http.Handler) HandlerFunc
而在使用gin.Default()返回的gin.Engine,会默认使用Recovery和Logger中间件,从gin.Default的源码可以看出来
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
如果不想使用默认中间件,可以使用gin.New()方法返回一个不带中间件的gin.Engine对象
总结
中间件是一个将具体业务和底层逻辑解耦的组件,在后端开发中有很多应用场景,像拦截、鉴权、日志等功能可以通过中间件简单的实现