To make sure error results are handled correctly across all requests I'm implementing a custom handler as described in http://blog.golang.org/error-handling-and-go. So instead of only accepting the w http.ResponseWriter, r *http.Request
params the handler optionally returns an error
.
I'm using Negroni and wondered whether I can set it up once to wrap all requests into handler
or if it will always have to be set up on a per-request basis as done for /
and /foo
in the following example?
type handler func(w http.ResponseWriter, r *http.Request) error
// ServeHTTP checks for error results and handles them globally
func (fn handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
http.Error(w, err, http.StatusInternalServerError)
}
}
// Index matches the `handler` type and returns an error
func Index(w http.ResponseWriter, r *http.Request) error {
return errors.New("something went wrong")
}
func main() {
router := mux.NewRouter()
// note how `Index` is wrapped into `handler`. Is there a way to
// make this global? Or will the handler(fn) pattern be required
// for every request?
router.Handle("/", handler(Index)).Methods("GET")
router.Handle("/foo", handler(Index)).Methods("GET")
n := negroni.New(
negroni.NewRecovery(),
negroni.NewLogger(),
negroni.Wrap(router),
)
port := os.Getenv("PORT")
n.Run(":" + port)
}
You can write a wrapper around r.Handle
if you want. You can't do it globally with Negroni as not all middleware you use assumes your handler
type.
e.g.
// Named to make the example clear.
func wrap(r *mux.Router, pattern string, h handler) *mux.Route {
return r.Handle(pattern, h)
}
func index(w http.ResponseWriter, r *http.Request) error {
io.WriteString(w, "Hello")
return nil
}
func main() {
r := mux.NewRouter()
wrap(r, "/", index)
http.ListenAndServe(":8000", r)
}
I'd argue that this is not much better than just explicitly type-casting your handlers (which is clear, if a little repetitive), or turning your handler type into a struct. The latter you can later extend to contain thread-safe fields (your DB pool, app config, etc) that you can then explicitly pass alongside each handler).
In reality your current router code is still clear and easy to read, and makes it obvious (to others) what type underpins your handlers.