Context-Aware Handler Chains in Go (using Stack)
As of Go 1.7 the context package is now part of Go's standard library and you should use that to pass data between your handlers. Joe Shaw has made a good tutorial here. If you're using a Go version < 1.7 then the information below may still be useful.
What do you mean by 'context-aware'?
If you're using a middleware pattern to process HTTP requests in Go, you may want to share some data or context between middleware handlers and your application handlers. For example you might want to:
- Use some middleware to create a CRSF token, and later render the token to a template in your application handler. Or perhaps...
- Authenticate a user in one middleware handler, and then pass the user details to a second middleware handler which checks if the user is authorised to access the resource.
There are a few packages that can help with this. Matt Silverlock has written a good article about some of the different approaches and tools – I won't rehash it here, instead I recommend giving it a read.
Why make another package?
Because none of the existing tools seemed ideal – at least to me.
gorilla/context is simple and very flexible, but relies on a global context map and you remembering to clear the context after each request. (It's still my favourite though).
goji provides request-scoped context, which is good, but it's part of a larger package and ties you into using the Goji router. The same is true of
gocraft/web, which also relies on reflection tricks under the hood that I struggle to wrap my head around.
I realised that the only time you need to worry about context is when you're chaining handlers together. So I looked at my favorite tool for chaining handlers, Alice, and began adapting that to create Stack.
I wanted the package to:
- Do a simple job, and then get out of the way.
- Provide a request-scoped context map.
- Let you create stackable, reusable, handler chains in the Alice style.
- Be as type-safe at compile time as it possibly could be.
- Be simple to understand and non-magic.
- Operate nicely with existing standards. In particular:
- The handler chain must satisfy the
http.Handlerinterface, so it can be used with the
- It should be compatible with the
func(http.Handler) http.Handlerpattern commonly used by third-party middleware packages.
The full documentation for Stack is here, but here's a quick example of how to use it: