I'm coding a web application in Go, and while various mux libraries provide a way to set a custom 404 error handler, there's nothing for other 4xx and 5xx error codes.
One suggestion is to override the WriteHeader method in the ResponseWriter interface and check the status code, but I'm confused as to how this would actually be written (the overriding of ResponseWriter methods, before outputting). One possible example can be found from the negroni package.
Would this be the correct way to serve a custom template for 4xx and 5xx errors? Could anyone provide an example of how this could be implemented?
Big thanks to David and elithrar for their responses and code. The Interceptor
struct that David coded can be used in a wrapper for the server mux, as elithrar shows in the comments. For those looking for further explanation as to why and how this works, this section from astaxie's book gives some very good info on the workings of the net/http package, along with viewing the server.go source code from the net/http package.
The mux libraries only have a means of setting the not found handler as a means of giving you a way to intercept requests where the mux can't resolve the URL to it's known mappings.
For example, you do:
mux.Handle("/foo",fooFunc)
mux.Handle("/bar",barFunc)
But the client accesses /baz
to which the mux has no mapping.
They do not actually intercept a 404 going to the client, they just invoke the not found handler when it runs in to this problem.
Also, if your /foo handler sends a 404 response, the not found does not get invoked.
If you want custom pages for various return responses from your mapped URLs, simply make the various handlers write the correct response.
If you don't control that logic (ie: the framwork is writing something and has no means to override), then you may want to intercept all requests and override the http.ResposeWriter with response code detection logic.
Here's an example interceptor that basically does what you want. On Play
package main
import (
"fmt"
"log"
)
import "net/http"
type Interceptor struct {
origWriter http.ResponseWriter
overridden bool
}
func (i *Interceptor) WriteHeader(rc int) {
switch rc {
case 500:
http.Error(i.origWriter, "Custom 500 message / content", 500)
case 404:
http.Error(i.origWriter, "Custom 404 message", 404)
case 403:
i.origWriter.WriteHeader(403)
fmt.Fprintln(i.origWriter, "Custom 403 message")
default:
i.origWriter.WriteHeader(rc)
return
}
// if the default case didn't execute (and return) we must have overridden the output
i.overridden = true
log.Println(i.overridden)
}
func (i *Interceptor) Write(b []byte) (int, error) {
if !i.overridden {
return i.origWriter.Write(b)
}
// Return nothing if we've overriden the response.
return 0, nil
}
func (i *Interceptor) Header() http.Header {
return i.origWriter.Header()
}