# 注册控制器风格的路由
所谓的注册控制器风格路由,可以理解为典型的 MVC 风格代码。即我们会在web
服务中声明各式各样的Controller
。
具体Controller
里面有什么 API,可以查看Controller API
# 基本用法
在 Beego 里面注册这种风格的路由很简单,只需要声明一个Controller
就可以:
import "github.com/beego/beego/v2/server/web"
type UserController struct {
web.Controller
}
2
3
4
5
6
这样我们就写好了一个Controller
。
如果我们要想添加一个方法,那么可以:
import "github.com/beego/beego/v2/server/web"
type UserController struct {
web.Controller
}
func (u *UserController) HelloWorld() {
u.Ctx.WriteString("hello, world")
}
func main() {
web.AutoRouter(&UserController{})
web.Run()
}
2
3
4
5
6
7
8
9
10
11
12
13
当我们访问 URL http://127.0.0.1:8080/user/helloworld
的时候,可以看到结果:
需要注意的是,控制器里面处理http
请求的方法必须是公共方法——即首字母大写,并且没有参数,没有返回值。如果你的方法不符合这个要求,大多数情况下,会发生panic
,例如你的方法有参数:
func (u *UserController) HelloWorldNoPtr(name string) {
u.Ctx.WriteString("don't work")
}
2
3
注意比较,在函数式注册风格里面,我们的
HandleFunc
其实是接收一个*Context
参数的
如果你的方法接收器不是指针:
golangci-lint run
func (u UserController) HelloWorldNoPtr() {
u.Ctx.WriteString("don't work")
}
2
3
这种写法也是可以的。一般的惯例是使用指针接收器,但是这并不强制。关于接收器的讨论,可以参考选择什么作为方法接收器
# Controller 的名字
在一些比较智能的 API 里面,我们会使用Controller
的名字来作为前缀、命名空间等。
那么,Controller
的名字是如何确定的呢?
在 Beego 里面,我们认为,一个Controller
的定义是形如:
type CtrlNameController struct {
}
2
3
比如说,我们定义了一个UserController
,那么Controller
的名字就是User
。如果大小写不敏感,那么user
也是合法的名字。
再比如说我们定义了一个BuyerRefundController
,那么BuyerRefund
就是名字,大小写不敏感的时候,buyerrefund
也是合法的名字。
# AutoRouter
刚才我们使用的是web
模块里面一个很实用的注册路由的方法AutoRouter
。
AutoRouter
解析出来的路由规则由RouterCaseSensitive
的值,Controller
的名字和方法名字共同决定。
其中UserController
它的名字是User
,而方法名字是HelloWorld
。那么:
- 如果
RouterCaseSensitive
为true
,那么AutoRouter
会注册两个路由,/user/helloworld/*
,/User/HelloWorld/*
; - 如果
RouterCaseSensitive
为false
,那么会注册一个路由,/user/helloworld/*
;
总而言之,在使用AutoRouter
的情况下,使用全小写的路径总是没错的。
# AutoPrefix
AutoRouter
内部是基于AutoPrefix
实现的,可以说,Controller
的名字,就是注册的前缀(prefix)。
下面我们来看一个简单的例子:
import (
"github.com/beego/beego/v2/server/web"
)
type UserController struct {
web.Controller
}
func (u *UserController) HelloWorld() {
u.Ctx.WriteString("Hello, world")
}
func main() {
// get http://localhost:8080/api/user/helloworld
// you will see return "Hello, world"
ctrl := &UserController{}
web.AutoPrefix("api", ctrl)
web.Run()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在运行之后,浏览器里面输入http://localhost:8080/api/user/helloworld
,就能看到返回的响应"Hello, world"。
类似于我们前面提到的AutoRoute
,这里注册的路由包含:
如果
RouterCaseSensitive
为true
,那么AutoPrefix
会注册两个路由,api/user/helloworld/*
,api/User/HelloWorld/*
;如果
RouterCaseSensitive
为false
,那么会注册一个路由,api/user/helloworld/*
;
这里我们可以总结出来一般性质的规则: 当我们使用AutoPrefix
的时候,注册的路由符合prefix/ctrlName/methodName
这种模式。
# 手动路由
如果我们并不想利用AutoRoute
或者AutoPrefix
来注册路由,因为这两个都依赖于Controller
的名字,也依赖于方法的名字。某些时候我们可能期望在路由上,有更强的灵活性。
在这种场景下,我们可以考虑说,采用手工注册的方式,挨个注册路由。
在 v2.0.2 我们引入了全新的注册方式。下面我们来看一个完整的例子
import (
"github.com/beego/beego/v2/server/web"
)
type UserController struct {
web.Controller
}
func (u *UserController) GetUserById() {
u.Ctx.WriteString("GetUserById")
}
func (u *UserController) UpdateUser() {
u.Ctx.WriteString("UpdateUser")
}
func (u *UserController) UserHome() {
u.Ctx.WriteString("UserHome")
}
func (u *UserController) DeleteUser() {
u.Ctx.WriteString("DeleteUser")
}
func (u *UserController) HeadUser() {
u.Ctx.WriteString("HeadUser")
}
func (u *UserController) OptionUsers() {
u.Ctx.WriteString("OptionUsers")
}
func (u *UserController) PatchUsers() {
u.Ctx.WriteString("PatchUsers")
}
func (u *UserController) PutUsers() {
u.Ctx.WriteString("PutUsers")
}
func main() {
// get http://localhost:8080/api/user/123
web.CtrlGet("api/user/:id", (*UserController).GetUserById)
// post http://localhost:8080/api/user/update
web.CtrlPost("api/user/update", (*UserController).UpdateUser)
// http://localhost:8080/api/user/home
web.CtrlAny("api/user/home", (*UserController).UserHome)
// delete http://localhost:8080/api/user/delete
web.CtrlDelete("api/user/delete", (*UserController).DeleteUser)
// head http://localhost:8080/api/user/head
web.CtrlHead("api/user/head", (*UserController).HeadUser)
// patch http://localhost:8080/api/user/options
web.CtrlOptions("api/user/options", (*UserController).OptionUsers)
// patch http://localhost:8080/api/user/patch
web.CtrlPatch("api/user/patch", (*UserController).PatchUsers)
// put http://localhost:8080/api/user/put
web.CtrlPut("api/user/put", (*UserController).PutUsers)
web.Run()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
需要注意的是,我们新的注册方法,要求我们传入方法的时候,传入的是(*YourController).MethodName
。这是因为 Go 语言特性,要求在接收器是指针的时候,如果你希望拿到这个方法,那么应该用(*YourController)
的形式。
那么,如果我们不用指针接收器:
import (
"github.com/beego/beego/v2/server/web"
)
type UserController struct {
web.Controller
}
func (u UserController) GetUserById() {
u.Ctx.WriteString("GetUserById")
}
func (u UserController) UpdateUser() {
u.Ctx.WriteString("UpdateUser")
}
func (u UserController) UserHome() {
u.Ctx.WriteString("UserHome")
}
func (u UserController) DeleteUser() {
u.Ctx.WriteString("DeleteUser")
}
func (u UserController) HeadUser() {
u.Ctx.WriteString("HeadUser")
}
func (u UserController) OptionUsers() {
u.Ctx.WriteString("OptionUsers")
}
func (u UserController) PatchUsers() {
u.Ctx.WriteString("PatchUsers")
}
func (u UserController) PutUsers() {
u.Ctx.WriteString("PutUsers")
}
func main() {
// get http://localhost:8080/api/user/123
web.CtrlGet("api/user/:id", UserController.GetUserById)
// post http://localhost:8080/api/user/update
web.CtrlPost("api/user/update", UserController.UpdateUser)
// http://localhost:8080/api/user/home
web.CtrlAny("api/user/home", UserController.UserHome)
// delete http://localhost:8080/api/user/delete
web.CtrlDelete("api/user/delete", UserController.DeleteUser)
// head http://localhost:8080/api/user/head
web.CtrlHead("api/user/head", UserController.HeadUser)
// patch http://localhost:8080/api/user/options
web.CtrlOptions("api/user/options", UserController.OptionUsers)
// patch http://localhost:8080/api/user/patch
web.CtrlPatch("api/user/patch", UserController.PatchUsers)
// put http://localhost:8080/api/user/put
web.CtrlPut("api/user/put", UserController.PutUsers)
web.Run()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
我们建议如果使用这个系列的方法,那么应该选择使用结构体接收器,这样代码看上去要清爽很多。
要额外引起注意的是CtrlAny
方法,这意味着,任意的http
方法都可以被处理。
# 注解路由
# 历史注册路由方式
和之前的注册路由方式比起来,我们这一次的改进,让用户可以在现代 IDE 中,点击方法名进行跳转。
历史上,我们的注册方式是这样的:
func main() {
ctrl := &MainController{}
// we register the path / to &MainController
// if we don't pass methodName as third param
// web will use the default mappingMethods
// GET http://localhost:8080 -> Get()
// POST http://localhost:8080 -> Post()
// ...
web.Router("/", ctrl)
// GET http://localhost:8080/health => ctrl.Health()
web.Router("/health", ctrl, "get:Health")
// POST http://localhost:8080/update => ctrl.Update()
web.Router("/update", ctrl, "post:Update")
// support multiple http methods.
// POST or GET http://localhost:8080/update => ctrl.GetOrPost()
web.Router("/getOrPost", ctrl, "get,post:GetOrPost")
// support any http method
// POST, GET, PUT, DELETE... http://localhost:8080/update => ctrl.Any()
web.Router("/any", ctrl, "*:Any")
web.Run()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
我们不再推荐使用这种方式,因为可读性和可维护性都不太好。特别是重构进行方法重命名的时候,容易出错。
# 相关内容
← 配置模块 注册函数式风格路由注册 →