In search of the best Go HTTP router for REST
May 15, 2015
I’m diving into REST APIs, and I want to implement one in Go, largely as a way to teach myself more about the language. One of the first problems I came up across (within an hour of starting coding) is that the HTTP router I selected (gorilla/mux) doesn’t have any way to issue a 405 - Method Not Allowed response. This is important for a proper REST API. (If you don’t know why this matters, I encourage you to read my recent post, How to learn REST: A resource guide).
This is not meant to be an exhaustive list of Go HTTP routers, in large part because I don’t want to use a full-fledged web “framework” like Martini or one of its competitors. Instead, I want a stand-alone HTTP router, with some basic functionality. My requirements are:
- Supports dynamic routes
- Supports returning 405 status codes, and other relevant status codes
- Makes it easy to do content negotiation (i.e. honors Accept: headers, etc)
- Easy to learn, understand, and use. (This one is somewhat subjective, so I list it last)
Once I find a Go HTTP router I’m happy with, I’ll probably stop updating this document. Until that time, I intend to archive my notes here, for the benefit of anyone else who may stumble upon them.
net/http is the official HTTP server package for Go, and it comes with its own built-in router. However, by design, it is incredibly minimalist. As such, it doesn’t support even dynamic routes. So clearly, this option is out, and it probably is for the vast majority of applications out there, too. It’s routing functionality is mainly useful for simple testing.
- Almost no features at all
The Gorilla Web Toolkit provides a number of packages for making web development in Go easier, but falls short of being a full-fledged “web framework.” It’s more like some loosely coupled (or uncoupled) tools, and you can pick and choose the ones you want.
This model was attractive to me, which is why I started down the road of using gorilla/mux for my Go HTTP router needs. But it falls short on my second requirement. There is some discussion on possible work-arounds, and even some apparently semi-mature code owned by the Gorilla team, which may help mitigate the problem. But it’s not officially supported. So my search continues, with the possibility of returning to gorilla/mux if I can’t find a better solution.
- Very popular, well documented
- Supports custom functions for route handling, so I can implement Method-based and content-type-based handlers if I need to
- Supports complex “subrouting” and stacking of route matching rules
- Doesn’t support 405 status codes
- Doesn’t support content-type negotiation
- Apparently fairly slow and a memory hog
Pat claims to support 405 responses automatically, in the comments of the code:
// Pat knows what methods are allowed given a pattern and a URI. For // convenience, PatternServeMux will add the Allow header for requests that // match a pattern for a method other than the method requested and set the // Status to "405 Method Not Allowed".
When I installed it, sure enough, it handled disallowed methods out of the box quite well. If I don’t specify a specific method handler, it assumes it’s disallowed, and responds with 405 if such a request is made.
- Basic support for HTTP 405 status
- Automatically supports HEAD verb for GET paths
- Doesn’t support the OPTIONS verb
- No support for content negotiation
- No support for custom match functions
- No support for stacking matching rules
This is touted to be the fastest httprouter available for Go. But the syntax it uses is a bit different than many others, which some people may think is problematic.
- Amazingly fast
- Supports 405 statuses natively
- Doesn’t work as cleanly with http.Handler (but it can be made to work)
- The default method handler doesn’t advertise an Accept: header
- No automatic support for HEAD or OPTIONS verbs
- No custom match function support
- No support for stacking matching rules
gorilla/handlers doesn’t require gorilla/mux, but it works well with it, and it mostly solves my 405 problem out of the box. The only downside so far is that it doesn’t automatically support the HEAD verb.
- Supports 405
- Doesn’t automatically support HEAD when GET is supported
- Doesn’t provide any scaffolding for content type negotiation
When did you last throw away unmerged work?
The last time you deleted or closed some unfinished work, what lead to that situation?
How clear is your product vision?
On a scale of 1 to "1000 songs in your pocket", how strong is your product vision?