[TOC] >[danger] # 控制器注册 以下章节的所有示例按照MVC模式进行目录管理(控制器需要分别通过独立的包```init()```方法进行自动注册),所有示例代码存放于:gitee.com/johng/gf/blob/master/geg/frame/mvc/ 目录中,每个示例无法独立运行(只是独立注册服务,没有```main```包),需要访问示例结果的话,需要执行外层的```main.go```入口程序。 >[success] ## 控制器注册 这种方式将每一个请求都当做一个控制器对象来处理,比较类似且媲美于PHP的请求执行模式,当一个请求进来之后,立即初始化一个新的控制器对象进行处理,处理完成之后释放控制器资源。这种服务注册方式的优点是简单、安全、OOP设计,每个请求的控制器严格数据隔离,成员变量无法相互共享。 我们可以通过```ghttp.BindController```方法完成控制器的注册。 gitee.com/johng/gf/blob/master/geg/frame/mvc/controller/demo/user.go ```go package demo import ( "gitee.com/johng/gf/g/net/ghttp" "gitee.com/johng/gf/g/frame/gmvc" ) // 定义业务相关的控制器对象, // 建议命名规范中控制器统一使用Controller前缀,后期代码维护时便于区分 type ControllerUser struct { gmvc.Controller } // 初始化控制器对象,并绑定操作到Web Server func init() { // 绑定控制器到指定URI,所有控制器的公开方法将会映射到指定URI末尾 // 例如该方法执行后,查看效果可访问: // http://127.0.0.1:8199/user/name // http://127.0.0.1:8199/user/age // http://127.0.0.1:8199/user/true-name ghttp.GetServer().BindController("/user", &ControllerUser{}) } // 定义操作逻辑 - 展示姓名 func (c *ControllerUser) Name() { c.Response.Write("John") } // 定义操作逻辑 - 展示年龄 func (c *ControllerUser) Age() { c.Response.Write("18") } // 定义操作逻辑 - 展示方法名称如果带多个单词,路由控制器使用英文连接符号"-"进行拼接 func (c *ControllerUser) TrueName() { c.Response.Write("John Smith") } ``` 服务注册必须提供注册的URI,注册时ghttp会将所有控制器的公开方法将会映射到指定URI末尾,具体参见示例代码说明。注册的控制器参数是一个```ghttp.Controller```接口,参数直接传递自定义的控制器对象指针即可(```&ControllerUser{}```,实际上只要继承了```gmvc.Controller```基类,控制器的指针对象便已经自动实现了```ghttpController```接口)。ghttp通过解析该对象指针获取对应的控制器方法,生成反射类型,处理请求时再根据该反射类型自动生成对应的控制器对象,处理客户端请求,处理完后自动销毁该控制器对象。 ### 多单词方法名 在这个示例中也展示了当方法带有多个单词时,路由控制器会自动使用英文连接符号"-"进行拼接,因此访问的时候方法名称需要带"-"号。 例如,方法名为```TrueName```时,生成的URI地址为```true-name```;方法名为```ShowListItems```,生成的URI地址为```show-list-items```;以此类推。对后续的执行对象注册方式同理。 ### 默认路由方法 控制器中的```Index```方法是一个特殊的方法,例如,当注册的路由规则为```/user```时,HTTP请求到```/user```时,将会自动映射到控制器的```Index```方法。也就是说,访问```/user```和```/user/index```将会达到相同的执行效果。对后续的执行对象注册方式同理。 ### 动态路由规则 控制器的注册方式支持动态路由,动态路由的介绍请参考后续的【[路由控制](路由控制.md)】章节。这里来一个示例看一下控制器注册的动态路由规则如何使用。 gitee.com/johng/gf/blob/master/geg/frame/mvc/controller/demo/rule.go ```go package demo import ( "gitee.com/johng/gf/g" "gitee.com/johng/gf/g/frame/gmvc" ) type ControllerRule struct { gmvc.Controller } func (c *ControllerRule) Show() { c.Response.Write(c.Request.Get("name")) } func init() { g.Server().BindController("/rule/{method}/:name", &ControllerRule{}) } ``` 启动外层的main.go,我们尝试着访问```http://127.0.0.1:8199/rule/show/john```,可以看到页面输出```john```。 在控制器注册以及执行对象注册中,路由规则支持特殊的变量```{method}```表示控制器或者执行对象的方法名称,如果路由规则中不使用该变量,那么默认的情况下,方法将会被追加到路由规则末尾。 >[success] ## 控制器方法注册 假如控制器中有若干公开方法,但是我只想注册其中几个,其余的方法我不想对外公开,怎么办? 实际开发中难免会遇到这种场景,当然ghttp也是支持这种需求。我们可以通过```ghttp.BindControllerMethod```方法完成对控制器指定方法的注册。相对于```ghttp.BindController```注册方法,```ghttp.BindControllerMetho```仅仅多了一个方法名称参数methods,参数支持传入多个方法名称,多个名称以英文“,”号分隔(**方法参数区分大小写**)。 看下面这个例子,执行后```ControllerMethod```的Name和Age方法将被注册到Web Server提供服务,而Info方法却不会对外公开。 gitee.com/johng/gf/blob/master/geg/frame/mvc/controller/demo/method.go ```go package demo import ( "gitee.com/johng/gf/g/net/ghttp" "gitee.com/johng/gf/g/frame/gmvc" ) type ControllerMethod struct { gmvc.Controller } func init() { ghttp.GetServer().BindControllerMethod("/method", &ControllerMethod{}, "Name, Age") } func (c *ControllerMethod) Name() { c.Response.Write("John") } func (c *ControllerMethod) Age() { c.Response.Write("18") } func (c *ControllerMethod) Info() { c.Response.Write("Info") } ``` 启动外层的main.go,我们尝试着访问```http://127.0.0.1:8199/method/info```,因为没有对该方法执行注册,因此会发现返回404;而```http://127.0.0.1:8199/method/name```及```http://127.0.0.1:8199/method/age```却能够正常访问。 >[success] ## RESTful控制器注册 RESTful设计方式的控制器,通常用于API服务。**在这种模式下,HTTP的Method将会映射到控制器对应的方法名称**,例如:POST方式将会映射到控制器的Post方法中,DELETE方式将会映射到控制器的Delete方法中。其他非HTTP Method命名的方法,即使是定义的包公开方法,将无法完成自动注册,对于应用端不可见。当然,如果控制器并未定义对应HTTP Method的方法,该Method请求下将会返回 ```HTTP Status 404```。此外,控制器方法名称需要保证是与HTTP Method相同的公开方法且方法名首字母大写。 这种方式注册的控制器,运行模式和“控制器注册”模式相同。我们可以通过```ghttp.BindControllerRest```方法完成RESTful控制器的注册。 以下是一个示例: gitee.com/johng/gf/blob/master/geg/frame/mvc/controller/demo/rest.go ```go package demo import ( "gitee.com/johng/gf/g/net/ghttp" "gitee.com/johng/gf/g/frame/gmvc" ) // 测试控制器 type ControllerRest struct { gmvc.Controller } // 初始化控制器对象,并绑定操作到Web Server func init() { // 控制器公开方法中与HTTP Method方法同名的方法将会自动绑定映射 ghttp.GetServer().BindControllerRest("/user", &ControllerRest{}) } // RESTFul - GET func (c *ControllerRest) Get() { c.Response.Write("RESTFul HTTP Method GET") } // RESTFul - POST func (c *ControllerRest) Post() { c.Response.Write("RESTFul HTTP Method POST") } // RESTFul - DELETE func (c *ControllerRest) Delete() { c.Response.Write("RESTFul HTTP Method DELETE") } // 该方法无法映射,将会无法访问到 func (c *ControllerRest) Hello() { c.Response.Write("Hello") } ``` >[success] ## Init与Shut回调方法 ```ghttp.Controller```接口中的```Init```和```Shut```是两个在HTTP请求流程中被Web Server自动调用的特殊方法(类似构造函数和析构函数的作用)。```gmvc.Controller```基类中已经实现了这两个方法,用户自定义的控制器类直接继承```gmvc.Controller```即可。**如果需要自定义请求初始化以及请求结束时的一些业务逻辑操作,可以在自定义控制器中重载这两个方法**。 1. ghttp.Controller接口 ```go type Controller interface { Init(*Request) Shut(*Request) Exit() } ``` 1. gmvc.Controller基类 ```go type Controller struct { Request *ghttp.Request // 请求数据对象 Response *ghttp.Response // 返回数据对象(r.Response) Server *ghttp.Server // Web Server对象(r.Server) Cookie *ghttp.Cookie // COOKIE操作对象 Session *ghttp.Session // SESSION操作对象 View *View // 视图对象 } ``` 1. Init回调方法 控制器初始化方法,参数是当前请求的对象。```gmvc.Controller```基类中的Init方法是对自身成员对象的初始化。 1. Shut回调方法 当请求结束时被Web Server自动调用,可以用于控制器一些收尾处理的操作。默认不执行任何操作。 >[success] ## Exit方法 可以看到我们的控制器中还有一个```Exit```方法,该方法的作用是标识该控制器已退出,不再执行后续的服务处理(但是不影响事件回调处理)。它仅仅是做一个标识作用,并不会马上停止服务执行。假如在```Init```回调方法中调用Exit,那么后续的服务逻辑将不会执行。假如在```Shut```方法中调用Exit,如果事件回调方法不做任何处理的话,那么该标识操作其实没有任何意义。