事件回调注册

    提供了事件回调注册功能,类似于其他框架的中间件功能,相比较于中间件,事件回调的特性更加简单。ghttp.Server支持用户对于某一事件进行自定义监听处理,按照pattern方式进行绑定注册(pattern格式与服务注册一致)。支持多个方法对同一事件进行监听,ghttp.Server将会按照路由优先级回调注册顺序进行回调方法调用。
    相关方法如下:

    当然域名对象也支持事件回调注册:

    1. func (d *Domain) BindHookHandler(pattern string, hook string, handler HandlerFunc) error
    2. func (d *Domain) BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error

    支持的Hook事件列表:

    1. BeforeServe/ghttp.HOOK_BEFORE_SERVE

      在进入/初始化服务对象之前。

    2. AfterServe/ghttp.HOOK_AFTER_SERVE

      在完成服务执行流程之后。

    3. BeforeOutput/ghttp.HOOK_BEFORE_OUTPUT

    4. AfterOutput/ghttp.HOOK_AFTER_OUTPUT

      向客户端输出返回内容之后。

    5. BeforeClose/ghttp.HOOK_BEFORE_CLOSE

      在http请求关闭之前(注意请求关闭是异步处理操作,没有在http执行流程中处理)。

    6. AfterClose/ghttp.HOOK_AFTER_CLOSE

      在http请求关闭之后(注意请求关闭是异步处理操作,没有在http执行流程中处理)。

    具体调用时机请参考图例所示。

    由于事件的绑定也是使用的路由规则,因此它的优先级和【路由控制】章节介绍的优先级是一样的。

    1. package main
    2. import (
    3. "gitee.com/johng/gf/g"
    4. "gitee.com/johng/gf/g/os/glog"
    5. "gitee.com/johng/gf/g/net/ghttp"
    6. )
    7. func main() {
    8. // 基本事件回调使用
    9. p := "/:name/info/{uid}"
    10. s := g.Server()
    11. s.BindHookHandlerByMap(p, map[string]ghttp.HandlerFunc{
    12. ghttp.HOOK_BEFORE_SERVE : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_BEFORE_SERVE) },
    13. ghttp.HOOK_AFTER_SERVE : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_AFTER_SERVE) },
    14. ghttp.HOOK_BEFORE_OUTPUT : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_BEFORE_OUTPUT) },
    15. ghttp.HOOK_AFTER_OUTPUT : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_AFTER_OUTPUT) },
    16. ghttp.HOOK_BEFORE_CLOSE : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_BEFORE_CLOSE) },
    17. ghttp.HOOK_AFTER_CLOSE : func(r *ghttp.Request){ glog.Println(ghttp.HOOK_AFTER_CLOSE) },
    18. })
    19. s.BindHandler(p, func(r *ghttp.Request) {
    20. })
    21. s.Run()
    22. }

    当访问http://127.0.0.1:8199/john/info/10000时,运行Web Server进程的终端将会按照事件的执行流程打印出对应的事件名称。

    通过事件1设置了访问/:name/info路由规则时的GET参数;通过事件2,改变了当访问的路径匹配路由/{object}/list/{page}.java时的输出结果。

    1. package main
    2. import (
    3. "gitee.com/johng/gf/g"
    4. "gitee.com/johng/gf/g/net/ghttp"
    5. "gitee.com/johng/gf/g/os/glog"
    6. )
    7. func main() {
    8. s := g.Server()
    9. s.BindHandler("/priority/show", func(r *ghttp.Request) {
    10. r.Response.Write("priority test")
    11. })
    12. s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc {
    13. "BeforeServe" : func(r *ghttp.Request) {
    14. glog.Println(r.Router.Uri)
    15. },
    16. })
    17. s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc {
    18. "BeforeServe" : func(r *ghttp.Request) {
    19. glog.Println(r.Router.Uri)
    20. },
    21. })
    22. s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc {
    23. "BeforeServe" : func(r *ghttp.Request) {
    24. glog.Println(r.Router.Uri)
    25. },
    26. })
    27. s.SetPort(8199)
    28. }

    在这个示例中,我们往注册了3个路由规则的事件回调,并且都匹配服务注册的地址/priority/show,这样我们便可以通过访问这个地址来看看路由执行的顺序是怎么样的。

    执行后我们访问http://127.0.0.1:8199/priority/show,随后我们可以在服务端的终端上看到以下输出信息:

    1. 2018-08-03 15:16:25.971 27391: http server started listening on [:8199]
    2. 2018-08-03 15:16:28.385 /priority/:name
    3. 2018-08-03 15:16:28.385 /priority/*any

    首先我们来看一个简单的REST接口示例:

    接口地址是http://localhost/api.v1/order,当然这个接口是不允许跨域的。我们打开一个不同的域名,例如:百度首页,然后按F12打开开发者面板,在console下执行以下AJAX请求:

    1. $.get("http://localhost:8199/api.v1/order", function(result){
    2. console.log(result)
    3. });

    结果如下:

    返回了不允许跨域的错误,接着我们修改一下测试代码,如下:

    1. package main
    2. import (
    3. "gitee.com/johng/gf/g"
    4. "gitee.com/johng/gf/g/frame/gmvc"
    5. "gitee.com/johng/gf/g/net/ghttp"
    6. )
    7. type Order struct {
    8. gmvc.Controller
    9. }
    10. func (o *Order) Get() {
    11. o.Response.Write("GET")
    12. }
    13. func main() {
    14. s := g.Server()
    15. s.BindHookHandlerByMap("/api.v1/*any", map[string]ghttp.HandlerFunc {
    16. "BeforeServe" : func(r *ghttp.Request) {
    17. r.Response.SetAllowCrossDomainRequest("*", "PUT,GET,POST,DELETE,OPTIONS")
    18. },
    19. })
    20. s.BindControllerRest("/api.v1/{.struct}", new(Order))
    21. s.SetPort(8199)
    22. s.Run()

    我们增加了针对于路由/api.v1/*any的绑定事件BeforeServe,该事件将会在所有服务执行之前调用,该事件的回调方法中,我们通过调用SetAllowCrossDomainRequest方法设置运行跨域请求。该绑定的事件路由规则使用了模糊匹配规则,表示所有/api.v1开头的接口地址都允许跨域请求。