跳到主要内容
版本:2.7.x

对象注册 是通过一个实例化的对象来执行路由注册,以后每一个请求都交给该对象(同一对象)处理, 该对象常驻内存不释放

相关方法

func (s *Server) BindObject(pattern string, object interface{}, methods ...string) error
func (s *Server) BindObjectMethod(pattern string, object interface{}, method string) error
func (s *Server) BindObjectRest(pattern string, object interface{}) error

前置约定:需要进行路由注册的方法必须为 公开方法,并且 方法定义 如下:

func(r *ghttp.Request)

否则无法完成注册,路由注册时将会有错误提示,例如:

panic: interface conversion: interface {} is xxx, not func(*ghttp.Request)

对象注册- BindObject

我们可以通过 BindObject 方法完成对象的注册。

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type Controller struct{}

func (c *Controller) Index(r *ghttp.Request) {
r.Response.Write("index")
}

func (c *Controller) Show(r *ghttp.Request) {
r.Response.Write("show")
}

func main() {
s := g.Server()
c := new(Controller)
s.BindObject("/object", c)
s.SetPort(8199)
s.Run()
}

可以看到,对象在进行路由注册时便生成了一个对象(对象在 Server 启动时便生成),此后不管多少请求进入, Server 都是将请求转交给该对象对应的方法进行处理。该示例执行后,终端输出的路由表如下:

SERVER  | DOMAIN  | ADDRESS | METHOD |     ROUTE     |         HANDLER          | MIDDLEWARE
|---------|---------|---------|--------|---------------|--------------------------|------------|
default | default | :8199 | ALL | /object | main.(*Controller).Index |
|---------|---------|---------|--------|---------------|--------------------------|------------|
default | default | :8199 | ALL | /object/index | main.(*Controller).Index |
|---------|---------|---------|--------|---------------|--------------------------|------------|
default | default | :8199 | ALL | /object/show | main.(*Controller).Show |
|---------|---------|---------|--------|---------------|--------------------------|------------|

随后可以通过 http://127.0.0.1:8199/object/show 查看效果。

提示

控制器中的 Index 方法是一个特殊的方法,例如,当注册的路由规则为 /user 时,HTTP请求到 /user 时,将会自动映射到控制器的 Index 方法。也就是说,访问地址 /user/user/index 将会达到相同的执行效果。

路由内置变量

当使用 BindObject 方法进行对象注册时,在路由规则中可以使用两个内置的变量: {.struct}{.method},前者表示当前 对象名称,后者表示当前注册的 方法名。我们来看一个例子:

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type Order struct{}

func (o *Order) List(r *ghttp.Request) {
r.Response.Write("list")
}

func main() {
s := g.Server()
o := new(Order)
s.BindObject("/{.struct}-{.method}", o)
s.SetPort(8199)
s.Run()
}

执行后,终端输出的路由表如下:

SERVER  | DOMAIN  | ADDRESS | METHOD |    ROUTE    |      HANDLER       | MIDDLEWARE
|---------|---------|---------|--------|-------------|--------------------|------------|
default | default | :8199 | ALL | /order-list | main.(*Order).List |
|---------|---------|---------|--------|-------------|--------------------|------------|

我们尝试着访问 http://127.0.0.1:8199/order-list ,可以看到页面输出 list。如果路由规则中不使用内置变量,那么默认的情况下,方法将会被追加到指定的路由规则末尾。

命名风格规则

通过对象进行路由注册时,可以根据对象及方法名称自动生成路由规则,默认的路由规则为:当方法名称带有多个 单词(按照字符大写区分单词)时,路由控制器默认会自动使用英文连接符号 - 进行拼接,因此访问的时候方法名称需要带 - 号。

例如,方法名为 UserName 时,生成的路由为 user-name;方法名为 ShowListItems 时,生成的路由为 show-list-items;以此类推。

此外,我们可以通过 .Server.SetNameToUriType 方法来设置对象方法名称的路由生成方式。支持的方式目前有 4 种,对应 4 个常量定义:

UriTypeDefault  = 0 // (默认)全部转为小写,单词以'-'连接符号连接
UriTypeFullName = 1 // 不处理名称,以原有名称构建成URI
UriTypeAllLower = 2 // 仅转为小写,单词间不使用连接符号
UriTypeCamel = 3 // 采用驼峰命名方式

注意:需要在通过对象进行路由注册前进行该参数的设置,在路由注册后设置将不会生效,那么将使用默认规则。

我们来看一个示例:

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type User struct{}

func (u *User) ShowList(r *ghttp.Request) {
r.Response.Write("list")
}

func main() {
u := new(User)
s1 := g.Server("UriTypeDefault")
s2 := g.Server("UriTypeFullName")
s3 := g.Server("UriTypeAllLower")
s4 := g.Server("UriTypeCamel")

s1.SetNameToUriType(ghttp.UriTypeDefault)
s2.SetNameToUriType(ghttp.UriTypeFullName)
s3.SetNameToUriType(ghttp.UriTypeAllLower)
s4.SetNameToUriType(ghttp.UriTypeCamel)

s1.BindObject("/{.struct}/{.method}", u)
s2.BindObject("/{.struct}/{.method}", u)
s3.BindObject("/{.struct}/{.method}", u)
s4.BindObject("/{.struct}/{.method}", u)

s1.SetPort(8100)
s2.SetPort(8200)
s3.SetPort(8300)
s4.SetPort(8400)

s1.Start()
s2.Start()
s3.Start()
s4.Start()

g.Wait()
}

为了对比演示效果,这个示例采用了 多Server 运行方式,将不同的名称转换方式使用了不同的 Server 来配置运行,因此我们可以方便地在同一个程序中,访问不同的 Server(通过不同的端口绑定)看到不同的结果。

执行后,终端输出的路由表如下:

      SERVER     | DOMAIN  | ADDRESS | METHOD |      ROUTE      |        HANDLER        | MIDDLEWARE
-----------------|---------|---------|--------|-----------------|-----------------------|-------------
UriTypeDefault | default | :8100 | ALL | /user/show-list | main.(*User).ShowList |
-----------------|---------|---------|--------|-----------------|-----------------------|-------------

SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
------------------|---------|---------|--------|----------------|-----------------------|-------------
UriTypeFullName | default | :8200 | ALL | /User/ShowList | main.(*User).ShowList |
------------------|---------|---------|--------|----------------|-----------------------|-------------

SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
------------------|---------|---------|--------|----------------|-----------------------|-------------
UriTypeAllLower | default | :8300 | ALL | /user/showlist | main.(*User).ShowList |
------------------|---------|---------|--------|----------------|-----------------------|-------------

SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
---------------|---------|---------|--------|----------------|-----------------------|-------------
UriTypeCamel | default | :8400 | ALL | /user/showList | main.(*User).ShowList |
---------------|---------|---------|--------|----------------|-----------------------|-------------

可以分别访问以下 URL 地址得到期望的结果:

http://127.0.0.1:8100/user/show-list
http://127.0.0.1:8200/User/ShowList
http://127.0.0.1:8300/user/showlist
http://127.0.0.1:8400/user/showList

对象方法注册

假如控制器中有若干公开方法,但是我只想注册其中几个,其余的方法我不想对外公开,怎么办?我们可以通过 BindObject 传递 第三个非必需参数 替换实现,参数支持传入 多个 方法名称,多个名称以英文 , 号分隔( 方法名称参数区分大小写)。

使用示例:

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type Controller struct{}

func (c *Controller) Index(r *ghttp.Request) {
r.Response.Write("index")
}

func (c *Controller) Show(r *ghttp.Request) {
r.Response.Write("show")
}

func main() {
s := g.Server()
c := new(Controller)
s.BindObject("/object", c, "Show")
s.SetPort(8199)
s.Run()
}

执行后,终端输出路由表为:

SERVER  | DOMAIN  | ADDRESS | METHOD |    ROUTE     |         HANDLER         | MIDDLEWARE
|---------|---------|---------|--------|--------------|-------------------------|------------|
default | default | :8199 | ALL | /object/show | main.(*Controller).Show |
|---------|---------|---------|--------|--------------|-------------------------|------------|

绑定路由方法- BindObjectMethod

我们可以通过 BindObjectMethod 方法绑定指定的路由到指定的方法执行( 方法名称参数区分大小写)。

提示

BindObjectMethodBindObject 的区别: BindObjectMethod 将对象中的指定方法与指定路由规则进行绑定,第三个 method 参数只能指定一个方法名称; BindObject 注册时,所有的路由都是对象方法名称按照规则生成的,第三个 methods 参数可以指定多个注册的方法名称。

来看一个例子:

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type Controller struct{}

func (c *Controller) Index(r *ghttp.Request) {
r.Response.Write("index")
}

func (c *Controller) Show(r *ghttp.Request) {
r.Response.Write("show")
}

func main() {
s := g.Server()
c := new(Controller)
s.BindObjectMethod("/show", c, "Show")
s.SetPort(8199)
s.Run()
}

执行后,终端输出的路由表如下:

SERVER  | DOMAIN  | ADDRESS | METHOD | ROUTE |         HANDLER         | MIDDLEWARE
|---------|---------|---------|--------|-------|-------------------------|------------|
default | default | :8199 | ALL | /show | main.(*Controller).Show |
|---------|---------|---------|--------|-------|-------------------------|------------|

RESTful 对象注册- BindObjectRest

RESTful 设计方式的控制器,通常用于 API 服务。 在这种模式下, HTTPMethod 将会映射到控制器对应的方法名称,例如: POST 方式将会映射到控制器的 Post 方法中(公开方法,首字母大写), DELETE 方式将会映射到控制器的 Delete 方法中,以此类推。其他非 HTTP Method 命名的方法,即使是定义的包公开方法,将不会自动注册,对于应用端不可见。当然,如果控制器并未定义对应 HTTP Method 的方法,该 Method 请求下将会返回 HTTP Status 404

注意

GoFrame 的这种 RESTful 对象注册方式是一种严格的 REST 路由注册方式,我们可以将控制器的对象看做 REST 中的资源,而其中的 HTTP Method 方法即为 REST 规范的资源操作方法。如果大家不太熟悉 REST 规范,或者不想太过严格的 RESTful 路由设计,那么请忽略该章节。

我们可以通过 BindObjectRest 方法完成 REST 对象的注册,示例:

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type Controller struct{}

// RESTFul - GET
func (c *Controller) Get(r *ghttp.Request) {
r.Response.Write("GET")
}

// RESTFul - POST
func (c *Controller) Post(r *ghttp.Request) {
r.Response.Write("POST")
}

// RESTFul - DELETE
func (c *Controller) Delete(r *ghttp.Request) {
r.Response.Write("DELETE")
}

// 该方法无法映射,将会无法访问到
func (c *Controller) Hello(r *ghttp.Request) {
r.Response.Write("Hello")
}

func main() {
s := g.Server()
c := new(Controller)
s.BindObjectRest("/object", c)
s.SetPort(8199)
s.Run()
}

执行后,终端输出路由表如下;

SERVER  | DOMAIN  | ADDRESS | METHOD |  ROUTE  |          HANDLER          | MIDDLEWARE
|---------|---------|---------|--------|---------|---------------------------|------------|
default | default | :8199 | DELETE | /object | main.(*Controller).Delete |
|---------|---------|---------|--------|---------|---------------------------|------------|
default | default | :8199 | GET | /object | main.(*Controller).Get |
|---------|---------|---------|--------|---------|---------------------------|------------|
default | default | :8199 | POST | /object | main.(*Controller).Post |
|---------|---------|---------|--------|---------|---------------------------|------------|

构造方法 Init 与析构方法 Shut

对象中的 InitShut 是两个在 HTTP 请求流程中被 Server 自动调用的特殊方法(类似 构造函数析构函数 的作用)。

  1. Init 回调方法

对象收到请求时的初始化方法,在服务接口调用之前被回调执行。

方法定义:

// "构造函数"对象方法
func (c *Controller) Init(r *ghttp.Request) {

}
  1. Shut 回调方法

当请求结束时被 Server 自动调用,可以用于对象执行一些收尾处理的操作。

方法定义:

// "析构函数"对象方法
func (c *Controller) Shut(r *ghttp.Request) {

}

举个例子:

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type Controller struct{}

func (c *Controller) Init(r *ghttp.Request) {
r.Response.Writeln("Init")
}

func (c *Controller) Shut(r *ghttp.Request) {
r.Response.Writeln("Shut")
}

func (c *Controller) Hello(r *ghttp.Request) {
r.Response.Writeln("Hello")
}

func main() {
s := g.Server()
c := new(Controller)
s.BindObject("/object", c)
s.SetPort(8199)
s.Run()
}

执行后,终端输出路由表如下:

SERVER  | DOMAIN  | ADDRESS | METHOD |     ROUTE     |         HANDLER          | MIDDLEWARE
|---------|---------|---------|--------|---------------|--------------------------|------------|
default | default | :8199 | ALL | /object/hello | main.(*Controller).Hello |
|---------|---------|---------|--------|---------------|--------------------------|------------|

可以看到,并没有自动注册 InitShut 这两个方法的路由,我们访问 http://127.0.0.1:8199/object/hello 后,输出结果为:

Hello
Shut