Tags: #go #http
In this comparison we’re not concerned about speed. Most of the approaches loop or switch through a list of routes (in contrast to fancy trie-lookup structures). All of these approaches only add a few microseconds to the request time, and that isn’t an issue in the majority of web applications.
Below are the opinions of the author of the article (linked above), while my (@integralist) own preference is towards “Regex Switch” due to the ease of implementation and the ease of understanding for engineers working on the code, as well as the extra flexibility you get over the “Regex Table” approach.
It’s basically a table of pre-compiled regexp objects with a little 21-line routing function that loops through them, and calls the first one that matches both the path and the HTTP method.
There’s nothing particularly clever about the regex table approach, and it’s similar to how a number of the third-party packages work. But it’s so simple it only takes a few lines of code and a few minutes to write. It’s also easy to modify if you need to: for example, to add logging, change the error responses to JSON, and so on.
The second approach still uses regexes, but with a simple imperative switch statement and a match()
helper to go through the matches. The advantage of this approach is that you can call other functions or test other things in each case. Also, the signature of the match
function allows you to “scan” path parameters into variables in order to pass them to the handlers more directly.
I must admit to being quite fond of this approach. I like how simple and direct it is, and I think the scan-like behaviour for path parameters is clean. Overall, despite liking the clarity of this approach and the scan-like match()
helper, a point against it is the messiness required to cache the regex compilation.
This approach is similar to the regex switch method, but instead of regexes it uses a simple, custom pattern matcher.
The patterns supplied to the custom match()
function handle one wildcard character, +
, which matches (and captures) any characters till the next /
in the request path. This is of course much less powerful than regex matching, but generally I’ve not needed anything more than “match till next slash” in my routes.
I quite like this approach (and it’s efficient), but the byte-by-byte matching code was a little fiddly to write – definitely not as simple as calling regex.FindStringSubmatch()
.
This approach simply splits the request path on /
and then uses a switch with case statements that compare the number of path segments and the content of each segment. It’s direct and simple, but also a bit error-prone, with lots of hard-coded lengths and indexes.
So while I like the bare-bones simplicity of this approach – just basic string equality comparisons – the verbosity of the matching and the error-prone integer constants would make me think twice about actually using it for anything but very simple routing.
A technique involving a small ShiftPath()
helper that returns the first path segment, and shifts the rest of the URL down. The current handler switches on the first path segment, then delegates to sub-handlers which do the same thing on the rest of the URL.
While I like the idea of just using the standard library, and the path-shifting technique is quite clever, I strongly prefer seeing my URLs all in one place – This approach spreads the logic across many handlers, so it’s difficult to see what handles what. It’s also quite a lot of code, some of which is error prone.
On balance, I find it too verbose and think it’d be difficult for people reading the code to quickly answer the question, “given this HTTP method and URL, what happens?”