GoFrame
提供了优雅的中间件请求控制方式,该方式也是主流的WebServer
提供的请求流程控制方式,基于中间件设计可以为WebServer
提供更灵活强大的插件机制。经典的中间件洋葱模型:
中间件的定义和普通HTTP执行方法HandlerFunc
一样,但是可以在Request
参数中使用Middleware
属性对象来控制请求流程。
我们拿一个跨域请求的中间件定义来示例说明一下:
func MiddlewareCORS(r *ghttp.Request) { r.Response.CORSDefault() r.Middleware.Next() } |
可以看到在该中间件中执行完成跨域请求处理的逻辑后,使用r.Middleware.Next()
方法进一步执行下一个流程;如果这个时候直接退出不调用r.Middleware.Next()
方法的话,将会退出后续的执行流程(例如可以用于请求的鉴权处理)。
中间件的类型分为两种:前置中间件和后置中间件。前置即在路由服务函数调用之前调用,后置即在其后调用。
其定义类似于:
func Middleware(r *ghttp.Request) { // 中间件处理逻辑 r.Middleware.Next() } |
其定义类似于:
func Middleware(r *ghttp.Request) { r.Middleware.Next() // 中间件处理逻辑 } |
中间件的注册有多种方式,参考接口文档: https://pkg.go.dev/github.com/gogf/gf/v2/net/ghttp
func (s *Server) Use(handlers ...HandlerFunc) |
全局中间件是可以独立使用的请求拦截方法,通过路由规则的方式进行注册,绑定到Server
上,由于中间件需要执行请求拦截操作,因此往往是使用"模糊匹配"或者"命名匹配"规则。
全局中间件仅对动态请求拦截有效,无法拦截静态文件请求。 |
func (g *RouterGroup) Middleware(handlers ...HandlerFunc) *RouterGroup |
分组路由中注册的中间件绑定到当前分组路由中的所有的服务请求上,当服务请求被执行前会调用到其绑定的中间件方法。 分组路由仅有一个Middleware
的中间件注册方法。分组路由中间件与全局中间件不同之处在于,分组路由中间件无法独立使用,必须在分组路由注册中使用,并且绑定到当前分组路由中所有的路由上作为路由方法的一部分。
由于全局中间件也是通过路由规则执行,那么也会存在执行优先级:
这里的建议来参考于 |
分组路由中间件是绑定到分组路由上的服务方法,不存在路由规则匹配,因此只会按照注册的先后顺序执行。参考后续示例或如下代码的执行结果。
package main import ( "context" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/glog" ) type HelloReq struct { g.Meta `path:"/hello" method:"get"` } type HelloRes struct { } type Hello struct{} func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) { return } func RequestHandle1(r *ghttp.Request) { glog.Debug(r.GetCtx(), "1") r.Middleware.Next() } func RequestHandle2(r *ghttp.Request) { glog.Debug(r.GetCtx(), "2") r.Middleware.Next() } func RequestHandle3(r *ghttp.Request) { glog.Debug(r.GetCtx(), "3") r.Middleware.Next() } func RequestHandle4(r *ghttp.Request) { glog.Debug(r.GetCtx(), "4") r.Middleware.Next() } func RequestHandle5(r *ghttp.Request) { r.Middleware.Next() glog.Debug(r.GetCtx(), "5") } func RequestHandle6(r *ghttp.Request) { r.Middleware.Next() glog.Debug(r.GetCtx(), "6") } func RequestHandle7(r *ghttp.Request) { r.Middleware.Next() glog.Debug(r.GetCtx(), "7") } func RequestHandle8(r *ghttp.Request) { r.Middleware.Next() glog.Debug(r.GetCtx(), "8") } func main() { s := g.Server() s.Use(ghttp.MiddlewareHandlerResponse) s.Group("/", func(g *ghttp.RouterGroup) { // 前置中间件 g.Middleware(RequestHandle1) g.Middleware(RequestHandle2) // 后置中间件 g.Middleware(RequestHandle5) g.Middleware(RequestHandle6) g.Group("/sub", func(g *ghttp.RouterGroup) { // 前置中间件 g.Middleware(RequestHandle3) g.Middleware(RequestHandle4) // 后置中间件 g.Middleware(RequestHandle7) g.Middleware(RequestHandle8) g.Bind(new(Hello)) }) }) s.Run() } |
执行结果如下:
如果在有分层的分组路由中使用中间件,同一个中间件只会执行一次,例如: