Namespace
namespace
,也叫做命名空间,是 Beego 提供的一种逻辑上的组织 API 的手段。 大多数时候,我们注册路由的时候,会按照一定的规律组织,那么使用namespace
就会使代码更加简洁可维护。
例如,我们整个应用分成两大块,一个是对安卓提供的 API,一个是对 IOS 提供的 API。那么整体上,就可以划分成两个命名空间;有一些应用会有版本概念,比如说早期是 V1,后面引入了 V2,再后来又引入了 V3,那么整个应用就有三个命名空间。不同版本的 API 注册在不同的命名空间之下。
namespace
稍微有点复杂,所以你可能需要多写几个简单的demo
来掌握它的用法。
例子
func main() {
uc := &UserController{}
// 创建 namespace
ns := web.NewNamespace("/v1",
web.NSCtrlGet("/home", (*MainController).Home),
web.NSRouter("/user", uc),
web.NSGet("/health", Health),
)
//注册 namespace
web.AddNamespace(ns)
web.Run()
}
type MainController struct {
web.Controller
}
func (mc *MainController) Home() {
mc.Ctx.WriteString("this is home")
}
type UserController struct {
web.Controller
}
func (uc *UserController) Get() {
uc.Ctx.WriteString("get user")
}
func Health(ctx *context.Context) {
ctx.WriteString("health")
}
在我们启动服务器之后,分别访问下面三个地址:
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
。
例如:
func main() {
uc := &UserController{}
// 初始化 namespace
ns := web.NewNamespace("/v1",
web.NSCtrlGet("/home", (*MainController).Home),
web.NSRouter("/user", uc),
web.NSGet("/health", Health),
// 嵌套 namespace
web.NSNamespace("/admin",
web.NSRouter("/user", uc),
),
)
//注册 namespace
web.AddNamespace(ns)
web.Run()
}
启动服务器,访问GET http://localhost:8080/v1/admin/user
就能看到输出。我们依旧可以看到,路径是各层namespace
拼接起来的。
namespace 的条件执行
Beego 的namespace
提供了一种条件判断机制。只有在符合条件的情况下,注册在该namespace
下的路由才会被执行。本质上,这只是一个filter
的应用。
例如,我们希望用户的请求的头部里面一定要带上一个x-trace-id
才会被后续的请求处理:
func main() {
uc := &UserController{}
// 初始化 namespace
ns := web.NewNamespace("/v1",
web.NSCond(func(b *context.Context) bool {
return b.Request.Header["x-trace-id"][0] != ""
}),
web.NSCtrlGet("/home", (*MainController).Home),
web.NSRouter("/user", uc),
web.NSGet("/health", Health),
// 嵌套 namespace
web.NSNamespace("/admin",
web.NSRouter("/user", uc),
),
)
//注册 namespace
web.AddNamespace(ns)
web.Run()
}
一般来说,我们现在也不推荐使用这个特性,因为它的功能和filter
存在重合,我们建议大家如果有需要,应该考虑自己正常实现一个filter
,代码可理解性会更高。该特性会考虑在未来的版本用一个filter
来替代,而后移除该方法。
Filter
namespace
同样支持filter
。该filter
只会作用于这个namespace
之下注册的路由,而对别的路由没有影响。
我们有两种方式添加Filter
,一个是在NewNamespace
中,调用web.NSBefore
或者web.NSAfter
,也可以调用ns.Filter()
func main() {
uc := &UserController{}
// 初始化 namespace
ns := web.NewNamespace("/v1",
web.NSCond(func(b *context.Context) bool {
return b.Request.Header["x-trace-id"][0] != ""
}),
web.NSBefore(func(ctx *context.Context) {
fmt.Println("before filter")
}),
web.NSAfter(func(ctx *context.Context) {
fmt.Println("after filter")
}),
web.NSCtrlGet("/home", (*MainController).Home),
web.NSRouter("/user", uc),
web.NSGet("/health", Health),
// 嵌套 namespace
web.NSNamespace("/admin",
web.NSRouter("/user", uc),
),
)
ns.Filter("before", func(ctx *context.Context) {
fmt.Println("this is filter for health")
})
//注册 namespace
web.AddNamespace(ns)
web.Run()
}
目前来说,namespace
对filter
的支持是有限的,只能支持before
和after
两种。
因此要支持复杂的filter
,或者filter-chain
,请参考过滤器
NSInclude
接下来我们讨论一个有点奇怪的东西,web.NSInclude
方法。该方法是注解路由的配套方法。
也就是意味着,它只对注解路由生效。
让我们来看一个简单的例子:
func init() {
api := web.NewNamespace("/api/v1",
web.NSNamespace("/goods",
web.NSInclude(
&controllers.GoodsController{},
),
),
)
web.AddNamespace(api)
}
注意到,我们这里的GoodsController
必然是一个注解路由的Controller
,而且已经使用bee
命令生成注解路由了。
如果不知道怎么定义注解路由controller
,或者使用bee
命令生成注解路由,请参考相关文档。