I am using Goji (https://github.com/zenazn/goji) and would like to define groups of routes that have their own middleware. For example, all paths under /company
should use an LDAP authentication and have a middleware defined to do this. All paths under /external
use a different type of authentication so they have a different middleware definition. But this is a single application served on the same port, so I don't want to create separate web services altogether -- just the paths (and some specific routes) may use different middleware.
All the examples I've seen with Goji are using a single set of middleware for all routes, so I am not sure how to accomplish this in a clean way. Additionally it would be nice if I could specify a base path for all routes within a route group, similar to how I've seen in some other routing frameworks.
Am I missing this functionality in the Goji library (or net/http by extension) that allows me to group routes together and have each group use its own middleware stack?
What I would like to achieve is something like this (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))
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With