I have a fairly quick-and-dirty error handler in my Go web app that raises a HTTP error, logs the important parts of the response and serves an error template. I'd like to remove the repetition where I'm writing something like this a few too many times in a handler:
err := doSomething()
if err != nil {
serverError(w, r, err, code)
}
I've had a good read of the Error Handling and Go article which covers defining a custom HTTP handler type that returns a error type/struct like this (or even returning int, err instead):
type appHandler func(http.ResponseWriter, *http.Request) *appError
type appError struct {
code int
Err error
}
// Ensures appHandler satisfies the http.Handler interface
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
switch err.Code {
case http.StatusNotFound:
http.NotFound(w, r)
case http.StatusInternalServerError:
http.Error(w, "message", http.StatusInternalServerError)
default:
http.Error(w, "message", err.Code)
}
}
}
But I'm not sure how to retain my existing middleware functionality/wrapper that allows me to chain middleware like this: r.HandleFunc("/route", use(myHandler, middleware1, middleware2))
where use
and my middleware look like this:
func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc {
for _, m := range middleware {
h = m(h)
}
return h
}
func AntiCSRF(h http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// do something
// h.ServeHTTP(w,r)
}
}
From what I can figure, it'd be something like the below (which doesn't work). I'm getting an error saying cannot use m(h) (type http.Handler) as type appHandler in assignment: need type assertion
. How do I resolve this whilst still keeping the middleware itself "as is"?
You can find a (simplified) playground example here: http://play.golang.org/p/Cmmo-wK2Af
r.Handle("/route", use(myHandler, middleware.NoCache)) // Contrived example!
func use(h myHandlerType?, middleware ...func(http.Handler) http.Handler) http.Handler {
for _, m := range middleware {
h = m(h)
}
return h
}
func myHandler(w http.ResponseWriter, r *http.Request) *appError {
// Extremely contrived example
name := "Matt"
_, err := fmt.Fprintf(w, "Hi %s", name)
if err != nil {
return &appError{500, err}
}
return nil
}
func contrivedMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "max-age=0, private, must-revalidate")
w.Header().Set("X-Accel-Expires", "0")
h.ServeHTTP(w, r)
})
}
What am I missing and is there a better way to do this?