ghttp.Server
提供了事件回调注册功能,类似于其他框架的 中间件
功能,相比较于 中间件
,事件回调的特性更加简单。
ghttp.Server
支持用户对于某一事件进行自定义监听处理,按照 pattern
方式进行绑定注册( pattern
格式与路由注册一致)。 支持多个方法对同一事件进行监听, ghttp.Server
将会按照 路由优先级
及 回调注册顺序
进行回调方法调用。同一事件时先注册的HOOK回调函数优先级越高。 相关方法如下:
func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFunc) error
func (s *Server) BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error
当然域名对象也支持事件回调注册:
func (d *Domain) BindHookHandler(pattern string, hook string, handler HandlerFunc) error
func (d *Domain) BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error
支持的 Hook
事件列表:
ghttp.HookBeforeServe
在进入/初始化服务对象之前,该事件是最常用的事件,特别是针对于权限控制、跨域请求等处理。
ghttp.HookAfterServe
在完成服务执行流程之后。
ghttp.HookBeforeOutput
向客户端输出返回内容之前。
ghttp.HookAfterOutput
向客户端输出返回内容之后。
具体调用时机请参考图例所示。
回调优先级
由于事件的绑定也是使用的路由规则,因此它的优先级和 路由管理-路由规则 章节介绍的优先级是一样的。
但是事件调用时和路由注册调用时的机制不一样, 同一个路由规则下允许绑定多个事件回调方法,该路由下的事件调用会 按照优先级进行调用
,假如优先级相等的路由规则,将会按照事件注册的顺序进行调用。
关于全局回调
我们往往使用绑定 /*
这样的 HOOK
路由来实现全局的回调处理,这样是可以的。但是 HOOK
执行的优先是最低的,路由注册的越精确,优先级越高,越模糊的路由优先级越低, /*
就属于最模糊的路由。
为降低不同的模块耦合性,所有的路由往往不是在同一个地方进行注册。例如用户模块注册的 HOOK
( /user/*
),它将会被优先调用随后才可能是全局的 HOOK
;如果仅仅依靠注册顺序来控制优先级,在模块多路由多的时候优先级便很难管理。
业务函数调用顺序
建议 相同的业务(同一业务模块) 的多个处理函数(例如: A、B、C)放到同一个 HOOK
回调函数中进行处理,在注册的回调函数中自行管理业务处理函数的调用顺序(函数调用顺序: A-B-C)。
虽然同样的需求,注册多个相同 HOOK
的回调函数也可以实现,功能上不会有问题,但从设计的角度来讲,内聚性降低了,不便于业务函数管理。
ExitHook
方法
当路由匹配到多个 HOOK
方法时,默认是按照路由匹配优先级顺序执行 HOOK
方法。当在 HOOK
方法中调用 Request.ExitHook
方法后,后续的 HOOK
方法将不会被继续执行,作用类似 HOOK
方法覆盖。
接口鉴权控制
事件回调注册比较常见的应用是在对调用的接口进行鉴权控制/权限控制。该操作需要绑定 ghttp.HookBeforeServe
事件,在该事件中会对所有匹配的接口请求(例如绑定 /*
事件回调路由)服务执行前进行回调处理。当鉴权不通过时,需要调用 r.ExitAll()
方法退出后续的服务执行(包括后续的事件回调执行)。
此外,在权限校验的事件回调函数中执行 r.Redirect*
方法,又没有调用 r.ExitAll()
方法退出业务执行,往往会产生 http multiple response writeheader calls
错误提示。因为 r.Redirect*
方法会往返回的header中写入 Location
头;而随后的业务服务接口往往会往header写入 Content-Type
/ Content-Length
头,这两者有冲突造成的。
中间件与事件回调
中间件( Middleware
)与事件回调( HOOK
)是 GF
框架的两大流程控制特性,两者都可用于控制请求流程,并且也都支持绑定特定的路由规则。但两者区别也是非常明显的。
- 首先,中间件侧重于应用级的流程控制,而事件回调侧重于服务级流程控制;也就是说中间件的作用域仅限于应用,而事件回调的“权限”更强大,属于
Server
级别,并可处理静态文件的请求回调。 - 其次,中间件设计采用了“洋葱”设计模型;而事件回调采用的是特定事件的钩子触发设计。
- 最后,中间件相对来说灵活性更高,也是比较推荐的流程控制方式;而事件回调比较简单,但灵活性较差。
Request.URL
与 Request.Router
Request.Router
是匹配到的路由对象,包含路由注册信息,一般来说开发者不会用到。 Request.URL
是底层请求的URL对象(继承自标准库 http.Request
),包含请求的URL地址信息,特别是 Request.URL.Path
表示请求的URI地址。