Having a web application with just one route isn’t very exciting… or useful! Let’s add a couple more routes so that the application starts to shape up like this:
|/||home||Display the home page|
|/snippet||showSnippet||Display a specific snippet|
|/snippet/create||createSnippet||Create a new snippet|
main.go file and update it as follows:
Make sure these changes are saved and then restart the web application:
If you visit the following links in your web browser you should now get the appropriate response for each route:
Fixed Path and Subtree Patterns
Now that the two new routes are up and running let’s talk a bit of theory.
Go’s servemux supports two different types of URL patterns: fixed paths and subtree paths. Fixed paths don’t end with a trailing slash, whereas subtree paths do end with a trailing slash.
Our two new patterns —
"/snippet/create" — are both examples of fixed paths. In Go’s servemux, fixed path patterns like these are only matched (and the corresponding handler called) when the request URL path exactly matches the fixed path.
In contrast, our pattern
"/" is an example of a subtree path (because it ends in a trailing slash). Another example would be something like
"/static/". Subtree path patterns are matched (and the corresponding handler called) whenever the start of a request URL path matches the subtree path. If it helps your understanding, you can think of subtree paths as acting a bit like they have a wildcard at the end, like
This helps explain why the
"/" pattern is acting like a catch-all. The pattern essentially means match a single slash, followed by anything (or nothing at all).
Restricting the Root URL Pattern
So what if you don’t want the
"/" pattern to act like a catch-all?
For instance, in the application we’re building we want the home page to be displayed if — and only if — the request URL path exactly matches
"/". Otherwise, we want the user to receive a
404 page not found response.
It’s not possible to change the behavior of Go’s servemux to do this, but you can include a simple check in the
home hander which ultimately has the same effect:
Go ahead and make that change, then restart the server and make a request for an unregistered URL path like
http://localhost:4000/missing. You should get a
404 response which looks a bit like this:
If you’ve been working with Go for a while you might have come across the
http.HandleFunc() functions. These allow you to register routes without declaring a servemux, like this:
Behind the scenes, these functions register their routes with something called the DefaultServeMux. There’s nothing special about this — it’s just regular servemux like we’ve already been using, but which is initialized by default and stored in a
net/http global variable. Here’s the relevant line from the Go source code:
Although this approach can make your code slightly shorter, I don’t recommend it for production applications.
DefaultServeMux is a global variable, any package can access it and register a route — including any third-party packages that your application imports. If one of those third-party packages is compromised, they could use
DefaultServeMux to expose a malicious handler to the web.
So, for the sake of security, it’s generally a good idea to avoid
DefaultServeMux and the corresponding helper functions. Use your own locally-scoped servemux instead, like we have been doing in this project so far.
Servemux Features and Quirks
In Go’s servemux, longer URL patterns always take precedence over shorter ones. So, if a servemux contains multiple patterns which match a request, it will always dispatch the request to the handler corresponding to the longest pattern. This has the nice side-effect that you can register patterns in any order and it won’t change how the servemux behaves.
Request URL paths are automatically sanitized. If the request path contains any
..elements or repeated slashes, the user will automatically be redirected to an equivalent clean URL. For example, if a user makes a request to
/foo/bar/..//bazthey will automatically be sent a
301 Permanent Redirectto
If a subtree path has been registered and a request is received for that subtree path without a trailing slash, then the user will automatically be sent a
301 Permanent Redirectto the subtree path with the slash added. For example, if you have registered the subtree path
/foo/, then any request to
/foowill be redirected to
Host Name Matching
It’s possible to include host names in your URL patterns. This can be useful when you want to redirect all HTTP requests to a canonical URL, or if your application is acting as the back end for multiple sites or services. For example:
When it comes to pattern matching, any host-specific patterns will be checked first and if there is a match the request will be dispatched to the corresponding handler. Only when there isn’t a host-specific match found will the non-host specific patterns also be checked.
What About RESTful Routing?
It’s important to acknowledge that the routing functionality provided by Go’s servemux is pretty lightweight. It doesn’t support routing based on the request method, it doesn’t support semantic URLs with variables in them, and it doesn’t support regexp-based patterns. If you have a background in using frameworks like Rails, Django or Laravel you might find this a bit restrictive… and surprising!
But don’t let that put you off. The reality is that Go’s servemux can still get you quite far, and for many applications is perfectly sufficient. For the times that you need more, there’s a huge choice of third-party routers that you can use instead of Go’s servemux. We’ll look at some of the popular options later in the book.