如何在Goji(Golang)中创建具有不同中间件的单独路由组?

时间:2014-08-14 01:21:05

标签: go goji

我正在使用Goji(https://github.com/zenazn/goji)并且想要定义具有自己的中间件的路由组。例如,/company下的所有路径都应使用LDAP身份验证并定义中间件来执行此操作。 /external下的所有路径使用不同类型的身份验证,因此它们具有不同的中间件定义。但这是在同一端口上提供的单个应用程序,因此我不想完全创建单独的Web服务 - 只是路径(和某些特定路由)可能使用不同的中间件。

我在Goji上看到的所有示例都是针对所有路由使用一组中间件,所以我不确定如何以干净的方式完成此任务。另外,如果我可以为路由组中的所有路由指定基本路径,那将会很好,类似于我在其他路由框架中看到的路径。

我是否在Goji库(或扩展名为net / http)中遗漏了这个功能,它允许我将路由组合在一起并让每个组使用自己的中间件堆栈?

我想要实现的是这样的事情(psedocode):

// Use an LDAP authenticator for:
// GET /company/employees
// and
// POST /company/records
companyGroup = &RouteGroup{"basePath": "/company"}
companyGroup.Use(LDAPAuthenticator)
companyGroup.Add(goji.Get("/employees", Employees.ListAll))
companyGroup.Add(goji.Post("/records", Records.Create))

// Use a special external user authenticator for: GET /external/products
externalGroup = &RouteGroup{"basePath": "/external"}
externalGroup.Use(ExternalUserAuthenticator)
externalGroup.Add(goji.Get("/products", Products.ListAll))

3 个答案:

答案 0 :(得分:8)

你应该可以通过以下方式解决问题:

// Use an LDAP authenticator 
companyGroup := web.New()
companyGroup.Use(LDAPAuthenticator)
companyGroup.Get("/company/employees", Employees.ListAll)
companyGroup.Post("/company/records", Records.Create)
goji.Handle("/company/*", companyGroup)

// Use a special external user authenticator for: GET /external/products
externalGroup := web.New()
externalGroup.Use(ExternalUserAuthenticator)
externalGroup.Get("/external/products", Products.ListAll)
goji.Handle("/external/*", externalGroup)

您需要为每个组提供自己的web。请记住,您需要在组成员中指定完整路径。

答案 1 :(得分:4)

Greg R的响应很好地总结了(并且是正确的答案),但我会向您展示一种让您“避免”(作弊!)必须指定完整路线的方法。

Goji的路由器速度快的部分原因是它在启动时编译所有内容,因此路由需要来了解它们的完整路径 - 但我们可以通过编写更高级别的函数来提供它“前缀”并返回路由器。

package main

import (
    "github.com/zenazn/goji/graceful"
    "github.com/zenazn/goji/web"
    "net/http"
)

func GetCompanyRoutes(prefix string) http.Handler {
    comp := web.New()
    comp.Use(SomeMiddleware)
    comp.Get(prefix+"/products", Products.ListAll)
    comp.Get(prefix+"/product/:id", Products.JustOne)
    comp.Get(prefix+"/product/delete", Products.Delete)

    return comp
}

// ... and a GetExternalRoutes with the same pattern

func main() {
    r := web.New()

    r.Get("/", IndexHandler)
    r.Handle("/company/*", GetCompanyRoutes("/company"))
    r.Handle("/external/*", GetExternalRoutes("/external"))

    graceful.Serve("localhost:8000", r)
}

由于这都是在启动时编译的,因此不会担心字符串连接会影响路由性能。

我使用类似的模式,因为我的处理程序驻留在一个单独的包中 - 我的包main只调用r.Handle("/admin/*", handlers.GetAdminRoutes("/admin")。如果我想在以后更改网址结构,我只需将其更改为r.Handle("/newadminlocation/*", handlers.GetAdminRoutes("/newadminlocation")

答案 2 :(得分:3)

根据Goji关于此closed issue的作者建议,您可以创建一个SubRouter结构,其扩展web.Mux,允许您提供与web.Mux相同的API除此之外,还使用调用go http.StripPrefix() func GetCompanyRoutes() http.Handler { comp := web.New() comp.Use(SomeMiddleware) comp.Get("/products", Products.ListAll) comp.Get("/product/:id", Products.JustOne) comp.Get("/product/delete", Products.Delete) return comp } func main() { r := web.New() r.Get("/", IndexHandler) companySubRouter := NewSubRouter("/company", r) companySubRouter.Handle("/*", GetCompanyRoutes()) externalSubRouter := NewSubRouter("/external", r) externalSubrouter.Handle("/*", GetExternalRoutes()) graceful.Serve("localhost:8000", r) } 的中间件去除子路由器的前缀。

上面的代码可以重写:

NewSubRouter()

type SubRouter struct { *web.Mux prefix string } func NewSubRouter(prefix string, parent *web.Mux) *SubRouter { mux := web.New() // we want prefix to be '/*'-suffix-free for http.StripPrefix() below. prefix = strings.TrimRight(prefix, "/*") // however, we bind parent to match both: // *-free prefix (exact match) parent.Handle(prefix, mux) // and match with a '/*' suffix, matching "prefix/*", e.g. "prefix/subpath/a" parent.Handle(prefix+"/*", mux) mux.Use(func(c *web.C, handler http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { // TODO record the prefix and possibly ancestors prefixes.. // strip the prefix from the URLs in the request for the following middleware strippedHandler := http.StripPrefix(prefix, handler) strippedHandler.ServeHTTP(rw, req) }) }) return &SubRouter{ Mux: mux, prefix: prefix, } } 的可能实施方式:

prefix

编辑:

  1. 我已更新prefix+"/*"< - > web.Mux接近上面是有点理智的。请注意,此函数的调用者需要提供尾部斜杠和星号免费前缀。领先的斜杠是可以的。

  2. 上述方法的替代方法是返回直线return mux(即return &SubRouter{...}而不是SubRouter,并完全丢弃prefix结构。这取决于{{1}}字符串是否对此函数的调用者具有任何值。