注册控制器风格的路由

所谓的注册控制器风格路由,可以理解为典型的 MVC 风格代码。即我们会在web服务中声明各式各样的Controller

具体Controller里面有什么 API,可以查看Controller API

基本用法

在 Beego 里面注册这种风格的路由很简单,只需要声明一个Controller就可以:

  1. import "github.com/beego/beego/v2/server/web"
  2. type UserController struct {
  3. web.Controller
  4. }

这样我们就写好了一个Controller

如果我们要想添加一个方法,那么可以:

  1. import "github.com/beego/beego/v2/server/web"
  2. type UserController struct {
  3. web.Controller
  4. }
  5. func (u *UserController) HelloWorld() {
  6. u.Ctx.WriteString("hello, world")
  7. }
  8. func main() {
  9. web.AutoRouter(&UserController{})
  10. web.Run()
  11. }

当我们访问 URL http://127.0.0.1:8080/user/helloworld的时候,可以看到结果:

hello world.png

需要注意的是,控制器里面处理http请求的方法必须是公共方法——即首字母大写,并且没有参数,没有返回值。如果你的方法不符合这个要求,大多数情况下,会发生panic,例如你的方法有参数:

  1. func (u *UserController) HelloWorldNoPtr(name string) {
  2. u.Ctx.WriteString("don't work")
  3. }

注意比较,在函数式注册风格里面,我们的HandleFunc其实是接收一个*Context参数的

如果你的方法接收器不是指针:

golangci-lint run

  1. func (u UserController) HelloWorldNoPtr() {
  2. u.Ctx.WriteString("don't work")
  3. }

这种写法也是可以的。一般的惯例是使用指针接收器,但是这并不强制。关于接收器的讨论,可以参考选择什么作为方法接收器

Controller 的名字

在一些比较智能的 API 里面,我们会使用Controller的名字来作为前缀、命名空间等。

那么,Controller的名字是如何确定的呢?

在 Beego 里面,我们认为,一个Controller的定义是形如:

  1. type CtrlNameController struct {
  2. }

比如说,我们定义了一个UserController,那么Controller的名字就是User。如果大小写不敏感,那么user也是合法的名字。

再比如说我们定义了一个BuyerRefundController,那么BuyerRefund就是名字,大小写不敏感的时候,buyerrefund也是合法的名字。

AutoRouter

刚才我们使用的是web模块里面一个很实用的注册路由的方法AutoRouter

AutoRouter解析出来的路由规则由RouterCaseSensitive的值,Controller的名字和方法名字共同决定。

其中UserController它的名字是User,而方法名字是HelloWorld。那么:

  • 如果RouterCaseSensitivetrue,那么AutoRouter会注册两个路由,/user/helloworld/*/User/HelloWorld/*
  • 如果RouterCaseSensitivefalse,那么会注册一个路由,/user/helloworld/*

总而言之,在使用AutoRouter的情况下,使用全小写的路径总是没错的

AutoPrefix

AutoRouter内部是基于AutoPrefix实现的,可以说,Controller的名字,就是注册的前缀(prefix)。

下面我们来看一个简单的例子:

  1. import (
  2. "github.com/beego/beego/v2/server/web"
  3. )
  4. type UserController struct {
  5. web.Controller
  6. }
  7. func (u *UserController) HelloWorld() {
  8. u.Ctx.WriteString("Hello, world")
  9. }
  10. func main() {
  11. // get http://localhost:8080/api/user/helloworld
  12. // you will see return "Hello, world"
  13. ctrl := &UserController{}
  14. web.AutoPrefix("api", ctrl)
  15. web.Run()
  16. }

在运行之后,浏览器里面输入http://localhost:8080/api/user/helloworld,就能看到返回的响应”Hello, world”。

类似于我们前面提到的AutoRoute,这里注册的路由包含:

  • 如果RouterCaseSensitivetrue,那么AutoPrefix会注册两个路由,api/user/helloworld/*api/User/HelloWorld/*

  • 如果RouterCaseSensitivefalse,那么会注册一个路由,api/user/helloworld/*

这里我们可以总结出来一般性质的规则: 当我们使用AutoPrefix的时候,注册的路由符合prefix/ctrlName/methodName这种模式。

手动路由

如果我们并不想利用AutoRoute或者AutoPrefix来注册路由,因为这两个都依赖于Controller的名字,也依赖于方法的名字。某些时候我们可能期望在路由上,有更强的灵活性。

在这种场景下,我们可以考虑说,采用手工注册的方式,挨个注册路由。

在 v2.0.2 我们引入了全新的注册方式。下面我们来看一个完整的例子

  1. import (
  2. "github.com/beego/beego/v2/server/web"
  3. )
  4. type UserController struct {
  5. web.Controller
  6. }
  7. func (u *UserController) GetUserById() {
  8. u.Ctx.WriteString("GetUserById")
  9. }
  10. func (u *UserController) UpdateUser() {
  11. u.Ctx.WriteString("UpdateUser")
  12. }
  13. func (u *UserController) UserHome() {
  14. u.Ctx.WriteString("UserHome")
  15. }
  16. func (u *UserController) DeleteUser() {
  17. u.Ctx.WriteString("DeleteUser")
  18. }
  19. func (u *UserController) HeadUser() {
  20. u.Ctx.WriteString("HeadUser")
  21. }
  22. func (u *UserController) OptionUsers() {
  23. u.Ctx.WriteString("OptionUsers")
  24. }
  25. func (u *UserController) PatchUsers() {
  26. u.Ctx.WriteString("PatchUsers")
  27. }
  28. func (u *UserController) PutUsers() {
  29. u.Ctx.WriteString("PutUsers")
  30. }
  31. func main() {
  32. // get http://localhost:8080/api/user/123
  33. web.CtrlGet("api/user/:id", (*UserController).GetUserById)
  34. // post http://localhost:8080/api/user/update
  35. web.CtrlPost("api/user/update", (*UserController).UpdateUser)
  36. // http://localhost:8080/api/user/home
  37. web.CtrlAny("api/user/home", (*UserController).UserHome)
  38. // delete http://localhost:8080/api/user/delete
  39. web.CtrlDelete("api/user/delete", (*UserController).DeleteUser)
  40. // head http://localhost:8080/api/user/head
  41. web.CtrlHead("api/user/head", (*UserController).HeadUser)
  42. // patch http://localhost:8080/api/user/options
  43. web.CtrlOptions("api/user/options", (*UserController).OptionUsers)
  44. // patch http://localhost:8080/api/user/patch
  45. web.CtrlPatch("api/user/patch", (*UserController).PatchUsers)
  46. // put http://localhost:8080/api/user/put
  47. web.CtrlPut("api/user/put", (*UserController).PutUsers)
  48. web.Run()
  49. }

需要注意的是,我们新的注册方法,要求我们传入方法的时候,传入的是(*YourController).MethodName。这是因为 Go 语言特性,要求在接收器是指针的时候,如果你希望拿到这个方法,那么应该用(*YourController)的形式。

那么,如果我们不用指针接收器:

  1. import (
  2. "github.com/beego/beego/v2/server/web"
  3. )
  4. type UserController struct {
  5. web.Controller
  6. }
  7. func (u UserController) GetUserById() {
  8. u.Ctx.WriteString("GetUserById")
  9. }
  10. func (u UserController) UpdateUser() {
  11. u.Ctx.WriteString("UpdateUser")
  12. }
  13. func (u UserController) UserHome() {
  14. u.Ctx.WriteString("UserHome")
  15. }
  16. func (u UserController) DeleteUser() {
  17. u.Ctx.WriteString("DeleteUser")
  18. }
  19. func (u UserController) HeadUser() {
  20. u.Ctx.WriteString("HeadUser")
  21. }
  22. func (u UserController) OptionUsers() {
  23. u.Ctx.WriteString("OptionUsers")
  24. }
  25. func (u UserController) PatchUsers() {
  26. u.Ctx.WriteString("PatchUsers")
  27. }
  28. func (u UserController) PutUsers() {
  29. u.Ctx.WriteString("PutUsers")
  30. }
  31. func main() {
  32. // get http://localhost:8080/api/user/123
  33. web.CtrlGet("api/user/:id", UserController.GetUserById)
  34. // post http://localhost:8080/api/user/update
  35. web.CtrlPost("api/user/update", UserController.UpdateUser)
  36. // http://localhost:8080/api/user/home
  37. web.CtrlAny("api/user/home", UserController.UserHome)
  38. // delete http://localhost:8080/api/user/delete
  39. web.CtrlDelete("api/user/delete", UserController.DeleteUser)
  40. // head http://localhost:8080/api/user/head
  41. web.CtrlHead("api/user/head", UserController.HeadUser)
  42. // patch http://localhost:8080/api/user/options
  43. web.CtrlOptions("api/user/options", UserController.OptionUsers)
  44. // patch http://localhost:8080/api/user/patch
  45. web.CtrlPatch("api/user/patch", UserController.PatchUsers)
  46. // put http://localhost:8080/api/user/put
  47. web.CtrlPut("api/user/put", UserController.PutUsers)
  48. web.Run()
  49. }

我们建议如果使用这个系列的方法,那么应该选择使用结构体接收器,这样代码看上去要清爽很多。

要额外引起注意的是CtrlAny方法,这意味着,任意的http方法都可以被处理。

注解路由

历史注册路由方式

和之前的注册路由方式比起来,我们这一次的改进,让用户可以在现代 IDE 中,点击方法名进行跳转。

历史上,我们的注册方式是这样的:

  1. func main() {
  2. ctrl := &MainController{}
  3. // we register the path / to &MainController
  4. // if we don't pass methodName as third param
  5. // web will use the default mappingMethods
  6. // GET http://localhost:8080 -> Get()
  7. // POST http://localhost:8080 -> Post()
  8. // ...
  9. web.Router("/", ctrl)
  10. // GET http://localhost:8080/health => ctrl.Health()
  11. web.Router("/health", ctrl, "get:Health")
  12. // POST http://localhost:8080/update => ctrl.Update()
  13. web.Router("/update", ctrl, "post:Update")
  14. // support multiple http methods.
  15. // POST or GET http://localhost:8080/update => ctrl.GetOrPost()
  16. web.Router("/getOrPost", ctrl, "get,post:GetOrPost")
  17. // support any http method
  18. // POST, GET, PUT, DELETE... http://localhost:8080/update => ctrl.Any()
  19. web.Router("/any", ctrl, "*:Any")
  20. web.Run()
  21. }

我们不再推荐使用这种方式,因为可读性和可维护性都不太好。特别是重构进行方法重命名的时候,容易出错。

相关内容

如何查看我注册的路由信息

控制器方法该选择哪个接收器 Controller API 路由规则——正确撰写路由