The http.Handler Interface
Before we go any further there’s a little theory that we should cover. It’s a bit complicated, so if you find this chapter hard-going don’t worry. Carry on with the application build and circle back to it later once you’re more familiar with Go.
In the previous chapters I’ve thrown around the term handler without explaining what it truly means. Strictly speaking, what we mean by handler is an object which satisfies the
In simple terms, this basically means that to be a handler an object must have a
ServeHTTP() method with the exact signature:
So in its simplest form a handler might look something like this:
Here we have an object (in this case it’s a
home struct, but it could equally be a string or function or anything else), and we’ve implemented a method with the signature
ServeHTTP(http.ResponseWriter, *http.Request) on it. That’s all we need to make a handler.
You could then register this with a servemux using the
Handle method like so:
Now, creating an object just so we can implement a
ServeHTTP() method on it is long-winded and a bit confusing. Which is why in practice it’s far more common to write your handlers as a normal function (like we have been so far). For example:
home function is just a normal function; it doesn’t have a
ServeHTTP() method. So in itself it isn’t a handler.
Instead we need to transform it into a handler using the
http.HandlerFunc() adapter, like so:
http.HandlerFunc() adapter works by automatically adding a
ServeHTTP() method to the
home function. When executed, this
ServeHTTP() method then simply calls the content of the original
home function. It’s a roundabout but convenient way of coercing a normal function into satisfying the
Throughout this project so far we’ve been using the
HandleFunc() method to register our handler functions with the servemux. This is just some syntactic sugar that transforms a function to a handler and registers it in one step, instead of having to do it manually. The code above is functionality equivalent to this:
The eagle-eyed of you might have noticed something interesting right at the start of this project. The
http.ListenAndServe() function takes a
http.Handler object as the second parameter…
… but we’ve been passing in a servemux.
We were able to do this because the servemux also has a
ServeHTTP() method, meaning that it too satisfies the
For me it simplifies things to think of the servemux as just being a special kind of handler, which instead of providing a response itself passes the request on to a second handler. This isn’t as much of a leap as it might first sound. Chaining handlers together is a very common idiom in Go, and something that we’ll do a lot of later in this project.
In fact, what exactly is happening is this: When our server receives a new HTTP request, it calls the servemux’s
ServeHTTP() method. This looks up the relevant handler based on the request URL path, and in turn calls that handler’s
ServeHTTP() method. You can think of a Go web application as a chain of
ServeHTTP() methods being called one after another.
Requests Are Handled Concurrently
There is one more thing that’s really important to point out: all incoming HTTP requests are served in their own goroutine. For busy servers, this means it’s very likely that the code in or called by your handlers will be running concurrently. While this helps make Go blazingly fast, the downside is that you need to be aware of (and protect against) race conditions when accessing shared resources from your handlers.