4

我正在使用 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))
4

3 回答 3

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. 请记住,您需要在组成员中指定完整路径。

于 2014-08-14T01:29:52.377 回答
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"). 如果我想在以后更改 URL 结构,我可以将其更改为r.Handle("/newadminlocation/*", handlers.GetAdminRoutes("/newadminlocation")

于 2014-08-14T02:25:39.750 回答
3

按照 Goji 关于这个已关闭问题的作者建议,您可以创建一个SubRouterextends 结构web.Mux,允许您提供与它相同的 API web.Mux,此外还可以使用调用 go's 的中间件去除子路由器的前缀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,
    }
}

编辑:

  1. 我已经将上面的prefix<->prefix+"/*"方法更新为有点理智。请注意,此函数的调用者需要提供尾部斜杠和无星号前缀。前导斜线是可以的。

  2. 上面的替代方法是返回一个顺子web.Mux(即return mux,而不是完全return &SubRouter{...}丢弃该SubRouter结构)。这取决于该prefix字符串对该函数的调用者是否具有任何值。

于 2014-09-09T15:12:48.460 回答