This claim might be a little too optimistic. I'd like to challenge you (everyone) to associate a User object with an http.Request. Example code in Node.js/ExpressJS:
function auth(req, res, next) {
req.User = User("username");
next();
}
This is such a simple example of a useful piece of middleware, yet it is so difficult to implement in Go in a 'painless and fun' way.
I would prefer to avoid contention on a global mutex.
I would prefer to keep the compatibility with http.Handler and http.HandlerFunc.
Maybe I'm missing something about your question, but I think my solution to this problem is simply to use closures; if there's per-request state (a user, a session, a data store), I write a function that returns an anonymous HandlerFunc that wraps up the real handler, sets up its environment, and passes it down.
This is "compatible" with http.Handler and http.HandlerFunc; everything is wired together as if it was vanilla handlers. But my actual handlers (or "actions" or whatever you'd like to call them) get state, persistence, &c.
To be fair, Express is a web framework. Go's net/http package is a little lower level than that.
Still, this is possible if you make up for the lack of a framework by writing some code yourself. I'd use (and I do use) gorilla/context[1] to allow me to pass data in the request context between middleware layers (i.e. CSRF middleware passes the token along).
You could write a couple of little helpers using gorilla/context (SetUser/GetUser, or more generically, SetString/GetString) and SetUser(username, r) in your middleware. The helpers (see the context docs!) just wrap a type assertion on the Get side, and set a map value on the Set side.
// Compatible with http.HandlerFunc
func auth(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
SetUser(r, "dave")
h.ServeHTTP(w, r)
}
}
// Routes
r.HandleFunc("/admin", use(adminHandler, auth)
http.Handle("/", r)
// Allows you to chain your middleware rather than wrapping functions
func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc {
for _, m := range middleware {
h = m(h)
}
return h
}
You can mod this to work with http.Handler if you want: replace HandlerFunc and then write r.Handle("/admin", use(http.HandlerFunc(adminHandler), auth) instead.
func (c *YourContext) UserRequired(rw web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) {
user := userFromSession(r) // Pretend like this is defined. It reads a session cookie and returns a *User or nil.
if user != nil {
c.User = user
next(rw, r)
} else {
rw.Header().Set("Location", "/")
rw.WriteHeader(http.StatusMovedPermanently)
// do NOT call next()
}
}
Compatibility with Handler/HandlerFunc is kept at the top level, but not at every step along the middleware chain.
The request context is I think the most annoying part of go http handling.
Oh you want to wrap your handler with something that does db.Begin()/db.Commit()? Well you need to stuff that tx somewhere...
so then your options are a global map[*http.Request]interface{} with mutexes or pass it as an arg. Passing it in as an arg means you either lose compile time type checking because you use reflect or lose the ability to add more middlewares that build up request context. The map[]interface{} also loses compile time type checking for the most part unless you do something per type of context, with more mutexes.
The second option is the sanest, but means either reflect if you have more than one context arg to build out (sql.Tx + cache + oauth grant + whatever else maybe?) or you have some common context object that may or may not use everything every request and your just adding more context members all the time that may or may not be initialized for that particular request.
Sometimes I miss the dynamic typing and/or compile time type templating.
I find the args approach the most appealing to me. Yeah you lose some compile time safety. Like you said the other option is a global context with mutexes which still somewhat misses some compile time safety. It basically boils down to reflection over type assertions.
I think Martini handles this pretty darn well if I do say so myself ;)
Author here. Thanks for your input, but I'd like to clarify some points.
The blog post's intent was not to show how to implement your own middleware mechanism in a web framework you're writing, but how to create HTTP middleware using the standard Go net/http interface.
Your proposed middleware type might feel a bit nicer, but it disregards the standard net/http interface and makes your middleware incompatible with it. Well, you could in theory make it compatible, but you would need some more "glue" and hacks. net/http doesn't really work the way you're proposing (returning a Response object, etc.). Please see my other post on the standard Go HTTP tools: http://justinas.org/embrace-gos-http-tools/
If you don't want full compatibility with existing net/http libraries, then sure – there are nicer ways. Again, my intention was to show how to do it with net/http and I agree that there are certain trade-offs.
"(note, I'd make the middlewares nesteable, passing a next middleware to each middleware, but that wasn't covered in the article)."
It was covered to some extent. In a model where every middleware is http.Handler itself, you nest your middlewares like:
finalHandler := Middleware1(Middleware2(MyApp))
So every middleware does actually get a reference to the next handler that should run.
In the Ruby community, the Rack interface has done a lot to enable Web middleware to work together. It would be great if there were an analogous interface for Go's net/http.
[+] [-] optymizer|12 years ago|reply
I would prefer to avoid contention on a global mutex.
I would prefer to keep the compatibility with http.Handler and http.HandlerFunc.
Good luck!
[+] [-] tptacek|12 years ago|reply
This is "compatible" with http.Handler and http.HandlerFunc; everything is wired together as if it was vanilla handlers. But my actual handlers (or "actions" or whatever you'd like to call them) get state, persistence, &c.
[+] [-] elithrar|12 years ago|reply
Still, this is possible if you make up for the lack of a framework by writing some code yourself. I'd use (and I do use) gorilla/context[1] to allow me to pass data in the request context between middleware layers (i.e. CSRF middleware passes the token along).
You could write a couple of little helpers using gorilla/context (SetUser/GetUser, or more generically, SetString/GetString) and SetUser(username, r) in your middleware. The helpers (see the context docs!) just wrap a type assertion on the Get side, and set a map value on the Set side.
You can mod this to work with http.Handler if you want: replace HandlerFunc and then write r.Handle("/admin", use(http.HandlerFunc(adminHandler), auth) instead.[1]: http://www.gorillatoolkit.org/pkg/context
[+] [-] cypriss|12 years ago|reply
The middleware would be, per the example there:
Compatibility with Handler/HandlerFunc is kept at the top level, but not at every step along the middleware chain.[+] [-] bradrydzewski|12 years ago|reply
It avoids the global lock and we haven't found any compatibility issues.
[+] [-] jemeshsu|12 years ago|reply
[+] [-] bfrog|12 years ago|reply
Oh you want to wrap your handler with something that does db.Begin()/db.Commit()? Well you need to stuff that tx somewhere...
so then your options are a global map[*http.Request]interface{} with mutexes or pass it as an arg. Passing it in as an arg means you either lose compile time type checking because you use reflect or lose the ability to add more middlewares that build up request context. The map[]interface{} also loses compile time type checking for the most part unless you do something per type of context, with more mutexes.
The second option is the sanest, but means either reflect if you have more than one context arg to build out (sql.Tx + cache + oauth grant + whatever else maybe?) or you have some common context object that may or may not use everything every request and your just adding more context members all the time that may or may not be initialized for that particular request.
Sometimes I miss the dynamic typing and/or compile time type templating.
[+] [-] codegangsta|12 years ago|reply
I think Martini handles this pretty darn well if I do say so myself ;)
[+] [-] acrispino|12 years ago|reply
[+] [-] latch|12 years ago|reply
Just because go's HTTP entry point is an http.HandlerFunc, doesn't mean your middleware need to implement that same method. I'd prefer:
No global variable, and definitely no contention against that global.(note, I'd make the middlewares nesteable, passing a next middleware to each middleware, but that wasn't covered in the article).
[+] [-] sjustinas|12 years ago|reply
The blog post's intent was not to show how to implement your own middleware mechanism in a web framework you're writing, but how to create HTTP middleware using the standard Go net/http interface.
Your proposed middleware type might feel a bit nicer, but it disregards the standard net/http interface and makes your middleware incompatible with it. Well, you could in theory make it compatible, but you would need some more "glue" and hacks. net/http doesn't really work the way you're proposing (returning a Response object, etc.). Please see my other post on the standard Go HTTP tools: http://justinas.org/embrace-gos-http-tools/
If you don't want full compatibility with existing net/http libraries, then sure – there are nicer ways. Again, my intention was to show how to do it with net/http and I agree that there are certain trade-offs.
"(note, I'd make the middlewares nesteable, passing a next middleware to each middleware, but that wasn't covered in the article)."
It was covered to some extent. In a model where every middleware is http.Handler itself, you nest your middlewares like:
So every middleware does actually get a reference to the next handler that should run.[+] [-] tedchs|12 years ago|reply
[+] [-] politician|12 years ago|reply
http://slid.es/andybons/high-performance-web-applications-in...