Namespace

namespace,也叫做命名空间,是 Beego 提供的一种逻辑上的组织 API 的手段。 大多数时候,我们注册路由的时候,会按照一定的规律组织,那么使用namespace就会使代码更加简洁可维护。

例如,我们整个应用分成两大块,一个是对安卓提供的 API,一个是对 IOS 提供的 API。那么整体上,就可以划分成两个命名空间;有一些应用会有版本概念,比如说早期是 V1,后面引入了 V2,再后来又引入了 V3,那么整个应用就有三个命名空间。不同版本的 API 注册在不同的命名空间之下。

namespace稍微有点复杂,所以你可能需要多写几个简单的demo来掌握它的用法。

例子

  1. func main() {
  2. uc := &UserController{}
  3. // 创建 namespace
  4. ns := web.NewNamespace("/v1",
  5. web.NSCtrlGet("/home", (*MainController).Home),
  6. web.NSRouter("/user", uc),
  7. web.NSGet("/health", Health),
  8. )
  9. //注册 namespace
  10. web.AddNamespace(ns)
  11. web.Run()
  12. }
  13. type MainController struct {
  14. web.Controller
  15. }
  16. func (mc *MainController) Home() {
  17. mc.Ctx.WriteString("this is home")
  18. }
  19. type UserController struct {
  20. web.Controller
  21. }
  22. func (uc *UserController) Get() {
  23. uc.Ctx.WriteString("get user")
  24. }
  25. func Health(ctx *context.Context) {
  26. ctx.WriteString("health")
  27. }

在我们启动服务器之后,分别访问下面三个地址:

  • GET http://localhost:8080/v1/home
  • GET http://localhost:8080/v1/user
  • GET http://localhost:8080/v1/health

都能得到对应输出。这些地址的规律可以总结为就是分段加起来。例如这个例子我们的namespace的前缀是v1,所以就是在注册的路由之前加上一段v1

注意到,在main函数里面,我们采用了不同的方式来注册路由。可以说,不管是函数式路由注册 还是 控制器路由注册,对于namespace来说都是可以的。整体规律就是多了NS这个前缀。

例如说web.Get对应到namespace内部注册,就是web.NSGet

同样的,我们也可以注册多个namespace,例如我们创建一个v2前缀的namespace

namespace 嵌套

有时候我们会希望namespace内部嵌套namespace。这个时候我们可以使用web.NSNamespace方法来注入一个子namespace

例如:

  1. func main() {
  2. uc := &UserController{}
  3. // 初始化 namespace
  4. ns := web.NewNamespace("/v1",
  5. web.NSCtrlGet("/home", (*MainController).Home),
  6. web.NSRouter("/user", uc),
  7. web.NSGet("/health", Health),
  8. // 嵌套 namespace
  9. web.NSNamespace("/admin",
  10. web.NSRouter("/user", uc),
  11. ),
  12. )
  13. //注册 namespace
  14. web.AddNamespace(ns)
  15. web.Run()
  16. }

启动服务器,访问GET http://localhost:8080/v1/admin/user就能看到输出。我们依旧可以看到,路径是各层namespace拼接起来的。

namespace 的条件执行

Beego 的namespace提供了一种条件判断机制。只有在符合条件的情况下,注册在该namespace下的路由才会被执行。本质上,这只是一个filter的应用。

例如,我们希望用户的请求的头部里面一定要带上一个x-trace-id才会被后续的请求处理:

  1. func main() {
  2. uc := &UserController{}
  3. // 初始化 namespace
  4. ns := web.NewNamespace("/v1",
  5. web.NSCond(func(b *context.Context) bool {
  6. return b.Request.Header["x-trace-id"][0] != ""
  7. }),
  8. web.NSCtrlGet("/home", (*MainController).Home),
  9. web.NSRouter("/user", uc),
  10. web.NSGet("/health", Health),
  11. // 嵌套 namespace
  12. web.NSNamespace("/admin",
  13. web.NSRouter("/user", uc),
  14. ),
  15. )
  16. //注册 namespace
  17. web.AddNamespace(ns)
  18. web.Run()
  19. }

一般来说,我们现在也不推荐使用这个特性,因为它的功能和filter存在重合,我们建议大家如果有需要,应该考虑自己正常实现一个filter,代码可理解性会更高。该特性会考虑在未来的版本用一个filter来替代,而后移除该方法。

Filter

namespace同样支持filter。该filter只会作用于这个namespace之下注册的路由,而对别的路由没有影响。

我们有两种方式添加Filter,一个是在NewNamespace中,调用web.NSBefore或者web.NSAfter,也可以调用ns.Filter()

  1. func main() {
  2. uc := &UserController{}
  3. // 初始化 namespace
  4. ns := web.NewNamespace("/v1",
  5. web.NSCond(func(b *context.Context) bool {
  6. return b.Request.Header["x-trace-id"][0] != ""
  7. }),
  8. web.NSBefore(func(ctx *context.Context) {
  9. fmt.Println("before filter")
  10. }),
  11. web.NSAfter(func(ctx *context.Context) {
  12. fmt.Println("after filter")
  13. }),
  14. web.NSCtrlGet("/home", (*MainController).Home),
  15. web.NSRouter("/user", uc),
  16. web.NSGet("/health", Health),
  17. // 嵌套 namespace
  18. web.NSNamespace("/admin",
  19. web.NSRouter("/user", uc),
  20. ),
  21. )
  22. ns.Filter("before", func(ctx *context.Context) {
  23. fmt.Println("this is filter for health")
  24. })
  25. //注册 namespace
  26. web.AddNamespace(ns)
  27. web.Run()
  28. }

目前来说,namespacefilter的支持是有限的,只能支持beforeafter两种。

因此要支持复杂的filter,或者filter-chain,请参考过滤器

NSInclude

接下来我们讨论一个有点奇怪的东西,web.NSInclude方法。该方法是注解路由的配套方法。

也就是意味着,它只对注解路由生效。

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

  1. func init() {
  2. api := web.NewNamespace("/api/v1",
  3. web.NSNamespace("/goods",
  4. web.NSInclude(
  5. &controllers.GoodsController{},
  6. ),
  7. ),
  8. )
  9. web.AddNamespace(api)
  10. }

注意到,我们这里的GoodsController必然是一个注解路由的Controller,而且已经使用bee命令生成注解路由了。

如果不知道怎么定义注解路由controller,或者使用bee命令生成注解路由,请参考相关文档。

相关文档

函数式路由注册 控制器路由注册 过滤器 注解路由