• 控制器注册
    • 控制器注册
      • 基本使用
      • 指定方法
    • 绑定路由方法
    • RESTful控制器注册
    • 构造方法Init与析构方法Shut
    • Exit, ExitAllExitHook

    为防止多种注册方式对开发者引起混淆,并且控制器注册方式效率较低,因此未来不再推荐使用控制器注册方式。

    控制器注册

    这种方式将每一个请求都当做一个控制器对象来处理,比较类似且媲美于PHP的请求执行模式,当一个请求进来之后,立即初始化一个新的控制器对象进行处理,处理完成之后释放控制器资源。这种路由注册方式的优点是简单、安全、OOP设计,每个请求的控制器严格数据隔离,成员变量无法相互共享。

    在这种模式下,我们可以给控制器struct对象定义一些非并发安全的成员变量,在请求中可以进行随意操作,不会影响并发安全性,因为请求一旦完成,所有的成员变量随着控制器对象销毁而销毁。

    控制器注册方式当然也支持{.struct}{.method}内置变量。

    控制器注册方式与执行对象注册方式非常类似,两者除了运行机制不一样以外,其他细节大同小异。建议在开始本章节阅读之前先了解一下执行对象注册方式。

    控制器注册

    基本使用

    我们可以通过BindController方法完成控制器的注册。

    github.com/gogf/gf/blob/master/geg/frame/mvc/controller/demo/user.go

    1. package demo
    2. import (
    3. "github.com/gogf/gf/g"
    4. "github.com/gogf/gf/g/frame/gmvc"
    5. )
    6. type User struct {
    7. gmvc.Controller
    8. }
    9. func init() {
    10. s := g.Server()
    11. s.BindController("/user", new(User))
    12. s.BindController("/user/{.method}/{uid}", new(User), "Info")
    13. s.BindController("/user/{.method}/{page}.html", new(User), "List")
    14. }
    15. func (u *User) Index() {
    16. u.Response.Write("User")
    17. }
    18. func (u *User) Info() {
    19. u.Response.Write("Info - Uid: ", u.Request.Get("uid"))
    20. }
    21. func (u *User) List() {
    22. u.Response.Write("List - Page: ", u.Request.Get("page"))
    23. }

    以上注册的路由列表:

    1. /user
    2. /user/index
    3. /user/info
    4. /user/list
    5. /user/{.method}/{uid}
    6. /user/{.method}/{page}.html

    路由注册必须提供注册的pattern,往往是一个精准匹配规则的URI,注册时会将所有控制器的公开方法映射到指定URI末尾(示例代码中,s.BindController("/user", new(User)),其中的new(User)也可以使用&User{}来替换)。注册的控制器参数是一个ghttp.Controller接口,参数直接传递自定义的控制器对象指针即可(&ControllerUser{},实际上只要继承了gmvc.Controller基类,控制器的指针对象便已经自动实现了ghttp.Controller接口)。

    该示例也展示了内置变量{.method}来使用方法名称,当注册的规则中带有内置变量时,路由将不会自动将方法名称追加到所给规则末尾,而是使用方法名称替换该内置变量

    框架通过解析该对象指针获取对应的控制器方法,生成反射类型,处理请求时再根据该反射类型自动生成对应的控制器对象,处理客户端请求,处理完后自动销毁该控制器对象。

    指定方法

    控制器方法注册原理类似于执行对象方法注册,只公开控制器中的特定方法。看下面这个例子,执行后ControllerMethodNameAge方法将被注册到Web Server提供服务,而Info方法却不会对外公开。

    1. package demo
    2. import (
    3. "github.com/gogf/gf/g"
    4. "github.com/gogf/gf/g/frame/gmvc"
    5. )
    6. type Method struct {
    7. gmvc.Controller
    8. }
    9. func init() {
    10. // 第三个参数指定主要注册的方法,其他方法不注册,方法名称会自动追加到给定路由后面,构成新路由
    11. // 以下注册会中注册两个新路由: /method/name, /method/age
    12. g.Server().BindController("/method", new(Method), "Name, Age")
    13. }
    14. func (c *Method) Name() {
    15. c.Response.Write("John")
    16. }
    17. func (c *Method) Age() {
    18. c.Response.Write("18")
    19. }
    20. func (c *Method) Info() {
    21. c.Response.Write("Info")
    22. }

    启动外层的main.go,我们尝试着访问http://127.0.0.1:8199/method/info,因为没有对该方法执行注册,因此会发现返回404;而http://127.0.0.1:8199/method/namehttp://127.0.0.1:8199/method/age却能够正常访问。这个例子比较简单,没有用复杂的动态路由,因此不在赘述。

    绑定路由方法

    注意BindControllerMethodBindController的区别:BindControllerMethod只是将控制器中的指定方法与指定路由规则进行绑定,第三个method参数只能指定一个方法名称;BindController是注册控制器,会自动按照方法命名生成一系列默认的路由规则(URI后缀形式),第三个methods参数可以指定多个注册的方法名称。

    1. package demo
    2. import (
    3. "github.com/gogf/gf/g"
    4. "github.com/gogf/gf/g/frame/gmvc"
    5. )
    6. type Method struct {
    7. gmvc.Controller
    8. }
    9. func init() {
    10. // 绑定路由到指定的方法执行,以下注册只会注册一个路由: /method-name
    11. g.Server().BindControllerMethod("/method-name", new(Method), "Name")
    12. }
    13. func (c *Method) Name() {
    14. c.Response.Write("John")
    15. }
    16. func (c *Method) Age() {
    17. c.Response.Write("18")
    18. }
    19. func (c *Method) Info() {
    20. c.Response.Write("Info")
    21. }

    RESTful控制器注册

    这种方式注册的控制器,运行模式和“控制器注册”模式相同。我们可以通过BindControllerRest方法完成RESTful控制器的注册。

    以下是一个示例:

    github.com/gogf/gf/blob/master/geg/frame/mvc/controller/demo/rest.go

    1. package demo
    2. import (
    3. "github.com/gogf/gf/g"
    4. "github.com/gogf/gf/g/frame/gmvc"
    5. )
    6. type Rest struct {
    7. gmvc.Controller
    8. }
    9. func init() {
    10. g.Server().BindControllerRest("/rest", &Rest{})
    11. }
    12. // RESTFul - GET
    13. func (c *Rest) Get() {
    14. c.Response.Write("RESTFul HTTP Method GET")
    15. }
    16. // RESTFul - POST
    17. func (c *Rest) Post() {
    18. c.Response.Write("RESTFul HTTP Method POST")
    19. }
    20. // RESTFul - DELETE
    21. func (c *Rest) Delete() {
    22. c.Response.Write("RESTFul HTTP Method DELETE")
    23. }
    24. // 该方法无法映射,将会无法访问到
    25. func (c *Rest) Hello() {
    26. c.Response.Write("Hello")
    27. }

    构造方法Init与析构方法Shut

    ghttp.Controller接口中的InitShut是两个在HTTP请求流程中被Web Server自动调用的特殊方法(类似构造函数和析构函数的作用)。gmvc.Controller基类中已经实现了这两个方法,用户自定义的控制器类直接继承gmvc.Controller即可。如果需要自定义请求初始化以及请求结束时的一些业务逻辑操作,可以在自定义控制器中重载这两个方法

    1. Init回调方法

      控制器初始化方法,参数是当前请求的对象。gmvc.Controller基类中的Init方法是对自身成员对象的初始化。

      方法定义:

      1. // "构造函数"控制器方法
      2. func (c *Controller) Init(r *ghttp.Request) {
      3. }
    2. Shut回调方法

      当请求结束时被Web Server自动调用,可以用于控制器一些收尾处理的操作。默认不执行任何操作。

      方法定义:

      1. // "析构函数"控制器方法
      2. func (c *Controller) Shut() {
      3. }

    Exit, ExitAllExitHook

    1. Exit: 仅退出当前执行的逻辑方法,如: 当前HOOK方法、服务方法,不退出后续的逻辑处理,可用于替代return
    2. ExitAll: 强行退出当前执行流程,当前执行方法的后续逻辑以及后续所有的逻辑方法将不再执行,常用于权限控制;
    3. ExitHook: 当路由匹配到多个HOOK方法时,默认是按照路由匹配优先级顺序执行HOOK方法。当在HOOK方法中调用ExitHook方法后,后续的HOOK方法将不会被继续执行,作用类似HOOK方法覆盖;