How can I create separate route groups with different middleware in Goji (Golang)?

試著忘記壹切 提交于 2019-12-05 16:16:02

You should be able to solve your problem with something like this:

// 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)

You need to give each group its own web. Just keep in mind you need to specify the full path within the group members.

Greg R's response sums it up nicely (and is the correct answer), but I'll show you an approach that lets you 'avoid' (cheat!) having to specify the full route.

Part of why Goji's router is fast is that it compiles everything on start-up, so routes need to know their full path - but we can provide that at a higher level by writing functions that take a "prefix" and return a router.

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)
}

Since this is all compiled at startup, there's no concern about the string concatenation impacting routing performance.

I use a similar pattern as my handlers reside in a separate package - my package main just calls r.Handle("/admin/*", handlers.GetAdminRoutes("/admin"). If I wanted to change the URL structure at a later date, I can just change it to r.Handle("/newadminlocation/*", handlers.GetAdminRoutes("/newadminlocation") instead.

Following Goji's author suggestion on this closed issue, you can create a SubRouter struct which extends web.Mux, allowing you to offer the same API as web.Mux does, and in addition strip the prefix for your subrouter using a middleware which calls go's http.StripPrefix().

The code above could be re-written:

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)
}

A possible implementation for 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,
    }
}

Edit:

  1. I've updated the prefix <-> prefix+"/*" approach above to be a bit sane-r. Note that callers to this function need to provide a trailing-slash and asterisk free prefix. leading slashes are ok.

  2. An alternative to the above is to return a straight web.Mux (i.e. return mux instead of return &SubRouter{...} and discard the SubRouter struct altogether). It depends on whether the prefix string is of any value to the caller of this function.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!