中间件在B/S模式中的架构的作用
中间件在B/S模式下起到了功能层的作用。当用户从WEB界面向服务器提交了数据请求或者应用请求时,功能层负责将这些请求分类为数据或应用请求,再向数据库发出数据交换申请。数据库对请求进行筛选处理之后,再将所需的数据通过功能层传递回到用户端。通过如此处理,单一用户可以进行点对面的操作,无需通过其他软件进行数据转换。
—节选自《中间件》百度百科
全局中间件、路由组中间件、单个路由中间件
**介绍:**全局中间件设置之后对全局的路由都起作用
**注册方式:**通过默认路由来设置。
分类:
router := gin.New()
//一次设置多个中间件
router.Use(Logger(), Recovery())
//一次设置一个中间件
router.Use(gin.Logger())
router.Use(gin.Recovery())
**介绍:**路由组中间件仅对该路由组下面的路由起作用
设置方式:
在声明路由组的同时设置路由组中间件
authorized := router.Group("/", CookieMiddleware())
先声明路由组然后再通过use
进行设置
authorized := router.Group("/")
authorized.Use(CookieMiddleware())
**介绍:**单个路由中间件仅对一个路由起作用
设置方式:
router.GET("/login", LoginMiddleware, handler)
router.GET("/mid", TimeMiddleware, CookieMiddleware(), handler)
在程序进入中间件的时候需要先进行一些处理,然后去 执行核心业务,在执行完核心业务之后再回来执行该中间件。
跟Java Spring 里面的AOP很像
router.GET("/mid", TimeMiddleware, handler)
// 记录函数执行的时间中间件
func TimeMiddleware(ctx *gin.Context) {
fmt.Println("------------TimeMiddleware 计时开始------------")
start := time.Now()
ctx.Next()
Since := time.Since(start)
fmt.Println(Since)
fmt.Println("++++++++++++TimeMiddleware 计时结束++++++++++++")
}
func handler(ctx *gin.Context) {
fmt.Println("#############正常的handler执行开始#############")
ctx.JSON(http.StatusOK, gin.H{
"ziop": "ziop",
})
}
在程序进入中间件之后我们进行了一些操作,判断该用户不满足访问这个请求的条件,这个时候我们就需要终止这个请求,不让其继续执行,这个时候就使用到了Abort
router.GET("/login", LoginMiddleware, handler)
func LoginMiddleware(ctx *gin.Context) {
fmt.Println("------------LoginMiddleware 计时开始------------")
login := ctx.Query("login")
if login != "" {
ctx.Next()
fmt.Println("Hi, " + login + ",欢迎访问ziop的小屋")
} else {
ctx.Abort()
fmt.Println("未登录,拒绝访问")
}
fmt.Println("++++++++++++LoginMiddleware 计时结束++++++++++++")
}
Gin中的中间件实际上还是一个Gin中的 gin.HandlerFunc
,也就是说他和其他的处理器都差不多,通过中间件的使用可以使多个处理器同时处理一个请求。
如果学过Javaweb或者说学过spring boot的同学可以将Gin里面的中间件理解为拦截器(filter)之类的工具。
有些网页在访问的时候需要用户登录之后才可以发送请求,这个时候我们就必须写一个处理器来检测用户是否登录如果登录的话就可以正常访问,如果用户没有登录就拒绝请求的处理。
起初,我们当让可以使用一个处理器来写: 这个处理器先判断一下用户是否登录然后再执行相应的处理。一个接口可以这样写,两个接口可以这样写。但是当需求量逐渐增多的时候我们发现这样写的话就会很麻烦。
这个时候我们发现可以抽出来一个公共的方法来进行验证用户是否登录。然后我们的核心业务代码就会大大减少了,同时也提升了开发的效率。这样我们对需要限制访问的方法加上这个公共的方法,不需要限制的方法不加上这个公共方法就简单的实现了我们的需求。
然而这个公共的方法就是我们这里要提到的中间件。
有时候我们想要检测一下某个请求的处理时间,来做一些日志,以便于以后有错误需要排查的时候方便排查。这时候就可以写一个中间件来检测处理时间。
router.GET("/mid", TimeMiddleware, handler)
// 记录函数执行的时间中间件
func TimeMiddleware(ctx *gin.Context) {
fmt.Println("------------TimeMiddleware 计时开始------------")
start := time.Now()
ctx.Next()
Since := time.Since(start)
fmt.Println(Since)
fmt.Println("++++++++++++TimeMiddleware 计时结束++++++++++++")
}
func handler(ctx *gin.Context) {
fmt.Println("#############正常的handler执行开始#############")
ctx.JSON(http.StatusOK, gin.H{
"ziop": "ziop",
})
}
我们有可能在正式处理核心业务之前会先对请求的数据进行一些处理,这个时候也可以使用到中间件。就像下面的案例就是在处理之前先进行了一次Cookie的设置。
router.GET("/mid", CookieMiddleware(), handler)
func CookieMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
fmt.Println("------------CookieMiddleware start------------")
user := ctx.Query("user")
fmt.Printf("ctx.Request.Host: %v\n", strings.Split(ctx.Request.Host, ":")[0])
ctx.SetCookie("user", user, 0, "", strings.Split(ctx.Request.Host, ":")[0], false, true)
fmt.Println("++++++++++++CookieMiddleware end++++++++++++")
}
}
package main
import (
"fmt"
"net/http"
"strings"
"time"
"github.com/fvbock/endless"
"github.com/gin-gonic/gin"
)
func main() {
// 新建一个没有任何默认中间件的路由
router := gin.New()
//----------------------------------------------------------------------
// 全局中间件
// 全局中间件
// Logger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。
// gin.DefaultWriter = os.Stdout 默认的日志输出是Stdout 也就是控制台输出
router.Use(gin.Logger())
// Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500。
// 这样的好处就是一旦有一个服务不能正常运行了,也只是会影响到一个请求。
// 不会影响到整个服务器的运行,不至于导致整个项目的瘫痪
router.Use(gin.Recovery())
//----------------------------------------------------------------------
//路有组中间件
// 认证路由组
// authorized := router.Group("/", AuthRequired())
// 和使用以下两行代码的效果完全一样:
authorized := router.Group("/admin")
// 路由组中间件! 在此例中,我们在 "authorized" 路由组中使用自定义创建的
// AuthRequired() 中间件
authorized.Use(TimeMiddleware)
{
authorized.POST("/login", handler)
authorized.POST("/submit", handler)
authorized.POST("/read", handler)
// 嵌套路由组
testing := authorized.Group("testing")
testing.GET("/analytics", handler)
}
//----------------------------------------------------------------------
//单个路由中间件
//单独为某个路由设置中间件
router.GET("/mid", TimeMiddleware, CookieMiddleware(), handler)
router.GET("/login", LoginMiddleware, handler)
// 优雅的退出服务
endless.ListenAndServe(":8000", router)
}
func handler(ctx *gin.Context) {
fmt.Println("#############正常的handler执行开始#############")
ctx.JSON(http.StatusOK, gin.H{
"ziop": "ziop",
})
}
func CookieMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
fmt.Println("------------CookieMiddleware start------------")
user := ctx.Query("user")
fmt.Printf("ctx.Request.Host: %v\n", strings.Split(ctx.Request.Host, ":")[0])
ctx.SetCookie("user", user, 0, "", strings.Split(ctx.Request.Host, ":")[0], false, true)
fmt.Println("++++++++++++CookieMiddleware end++++++++++++")
}
}
// 记录函数执行的时间中间件
func TimeMiddleware(ctx *gin.Context) {
fmt.Println("------------TimeMiddleware 计时开始------------")
start := time.Now()
ctx.Next()
Since := time.Since(start)
fmt.Println(Since)
fmt.Println("++++++++++++TimeMiddleware 计时结束++++++++++++")
}
// 登录检测中间件
func LoginMiddleware(ctx *gin.Context) {
fmt.Println("------------LoginMiddleware 计时开始------------")
login := ctx.Query("login")
if login != "" {
ctx.Next()
fmt.Println("Hi, " + login + ",欢迎访问ziop的小屋")
} else {
ctx.Abort()
fmt.Println("未登录,拒绝访问")
}
fmt.Println("++++++++++++LoginMiddleware 计时结束++++++++++++")
}