At this point our API is sending nicely formatted JSON responses for successful requests, but if a client makes a bad request — or something goes wrong in our application — we’re still sending them a plain-text error message from the http.Error() and http.NotFound() functions.
In this chapter we’ll fix that by creating some additional helpers to manage errors and send the appropriate JSON responses to our clients.
If you’re following along, go ahead and create a new cmd/api/errors.go file:
And then add some helper methods like so:
Now those are in place, let’s update our API handlers to use these new helpers instead of the http.Error() and http.NotFound() functions. Like so:
Any error messages that our own API handlers send will now be well-formed JSON responses. Which is great!
But what about the error messages that httprouter automatically sends when it can’t find a matching route? By default, these will still be the same plain-text (non-JSON) responses that we saw earlier in the book.
Fortunately, httprouter allows us to set our own custom error handlers when we initialize the router. These custom handlers must satisfy the http.Handler interface, which is good news for us because it means we can easily re-use the notFoundResponse() and methodNotAllowedResponse() helpers that we just made.
Open up the cmd/api/routes.go file and configure the httprouter instance like so:
Let’s test out these changes. Restart the application, then try making some requests for endpoints that don’t exist, or which use an unsupported HTTP method. You should now get some nice JSON error responses which look similar to these:
In this final example, notice that httprouter still automatically sets the correct Allow header for us, even though it is now using our custom error handler for the response?
System-generated error responses
While we’re on the topic of errors, I’d like to mention that in certain scenarios Go’s http.Server may still automatically generate and send plain-text HTTP responses. These scenarios include when:
The HTTP request specifies an unsupported HTTP protocol version.
The HTTP request contains a missing or invalid Host header, or multiple Host headers.
The HTTP request contains an invalid header name or value.
The HTTP request contains an unsupported Transfer-Encoding header.
The size of the HTTP request headers exceeds the server’s MaxHeaderBytes setting.
The client makes a HTTP request to a HTTPS server.
For example, if we try sending a request with an invalid Host header value we will get a response like this:
Unfortunately, these responses are hard-coded into the Go standard library, and there’s nothing we can do to customize them to use JSON instead.
But while this is something to be aware of, it’s not necessarily something to worry about. In a production environment it’s relatively unlikely that well-behaved, non-malicious, clients would trigger these responses anyway, and we shouldn’t be overly concerned if bad clients are sometimes sent a plain-text response instead of JSON.