Gin中的中间件和我们通常所认识的中间件如缓存中间件(Redis)、消息中间件(Kafaka、MQ)等不一样,Go语言中的中间件更像Spring中的拦截器,根据作用范围又分为全局中间件和局部中间件,下面对中间件这部分进行介绍。Gin框架文档地址:Gin框架文档
在Gin框架中,gin.Default()
默认使用了Logger
和Recovery
中间件,其中Logger中间件将日志写入gin.DefaultWriter
,即使配置了GIN_MODE=release
。而Recovery
中间件会recover任何panic。如果有panic的话,会写入500响应码。如果不想使用上面两个默认的中间件,可以使用gin.New()
新建一个没有任何默认中间件的路由。在Gin中,使用gin.HandlerFunc
定义一个中间件,比如上面提到的两个中间件,Logger
和Recovery
,二者定义如下:
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
}
// 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{})
}
再看看HandlerFun
函数:
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
因此我们知道了,在Gin框架中,中间件实际上就是一个以gin.Context
为形参的函数而已,与我们定义处理HTTP请求的Handler本质上是一样的。
全局中间件作用范围为所有的请求,使用Use函数来注册一个全局的中间件:
func StatCostHandlerFun() gin.HandlerFunc {
return func(c *gin.Context) {
//header := c.Request.Header //获取请求头
//strings := header["cookie"]
start := time.Now()
//c.Header("d","")
c.Set("name", "小王子") //通过c.Set在请求上下文中设置值,后续处理函数能够取到该值,如设置登录校验过了...
// Next方法调用该请求的剩余handler
c.Next()
//c.Abort() //Abort不调用该请求的处理函数
//计算耗时
cost := time.Since(start)
log.Println(cost)
}
}
func main() {
r := gin.Default() //默认路由
r.Use(StatCostHandlerFun()) //注册全局中间件
r.GET("/global", func(c *gin.Context) {
// 取值
name, _ := c.Get("name")
//name := c.MustGet("name").(string) // 从上下文取值
// 页面接收
c.JSON(200, gin.H{"name": name})
})
r.Run(":8888")
}
在各种路由中,handler是可以定义多个的,可以为一个路由定义多个handler方法,因此才有上面的Next或者Abort方法的使用。
如果要在中间件或者handler中使用goruntine时候,不能直接使用原始的上下文,而只能利用Copy函数使用上下文的拷贝。
如:
func handler1(c *gin.Context) {
name := c.MustGet("name").(string) // 从上下文取值
//name1 ,ok := c.Get("name")
log.Println(name)
go dealF(c.Copy()) //另外的协程中只能使用拷贝的不能修改
//go func(c *gin.Context) {
// //
//}(c.Copy())
c.JSON(http.StatusOK, gin.H{
"msg": "handler1",
})
}
func dealF(c *gin.Context){
xxxx
}
r.GET("testA", handler1)
局部中间件只作用于特定的请求,在路由中直接定义即可。
func StatCostHandlerFun() gin.HandlerFunc {
return func(c *gin.Context) {
//header := c.Request.Header //获取请求头
//strings := header["cookie"]
start := time.Now()
//c.Header("d","")
c.Set("name", "小王子") //通过c.Set在请求上下文中设置值,后续处理函数能够取到该值,如设置登录校验过了...
// Next方法调用该请求的剩余handler
c.Next()
//c.Abort() //Abort不调用该请求的处理函数
//计算耗时
cost := time.Since(start)
log.Println(cost)
}
}
func handler1(c *gin.Context) {
name := c.MustGet("name").(string) // 从上下文取值
//name1 ,ok := c.Get("name")
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"msg": "handler1",
})
}
func main() {
r := gin.Default() //默认路由
r.GET("local", StatCostHandlerFun(), handler1) //单独注册中间件
r.GET("testA", func(c *gin.Context) {
c.JSON(200, gin.H{"data": 233})
})
r.Run(":8888")
}
中间件执行顺序:
更多的时候,我们会根据业务不同划分不同路由分組(RouterGroup ),不同的路由分组再应用不同的中间件,这样就达到了不同的请求由不同的中间件进行拦截处理。
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
// 定义中间
func StatCostHandlerFun(c *gin.Context) {
start := time.Now()
c.Next()
// 统计时间
since := time.Since(start)
fmt.Println("程序用时:", since)
}
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// 注册中间件
r.Use(StatCostHandlerFun)
shoppingGroup := r.Group("/shopping")
{
shoppingGroup.GET("/index", shopIndexHandler)
shoppingGroup.GET("/home", shopHomeHandler)
}
r.Run(":8000")
}
func shopIndexHandler(c *gin.Context) {
time.Sleep(5 * time.Second)
}
func shopHomeHandler(c *gin.Context) {
time.Sleep(3 * time.Second)
}
中间件的最大作用就是拦截过滤请求,比如我们有些请求需要用户登录或者需要特定权限才能访问,这时候便可以中间件中做过滤拦截,当用户请求不合法时,可以使用下面列出的gin.Context的几个方法中断用户请求,下面三个方法中断请求后,直接返回200,但响应的body中不会有数据。
func (c *Context) Abort()
func (c *Context) AbortWithError(code int, err error) *Error
func (c *Context) AbortWithStatus(code int)
使用AbortWithStatusJSON()方法,中断用户请求后,则可以返回json格式的数据:
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{})
前面的都是到达我们定义的HTTP处理方法前进行拦截,其实,如果在中间件中调用gin.Context的Next()方法,则可以请求到达并完成业务处理后,再经过中间件后置拦截处理,Next()方法定义如下:
func (c *Context) Next()
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
添加我为好友,拉您入交流群!
请使用微信扫一扫!