Handler
Usage
Handler is a collection of common http.Handlers used in Go web applications, typically REST APIs.
These handlers integrate well with applications that use the Handler Constructor pattern:
type Constructor func(r *http.Request) http.HandlerPlease see the example for a working example.
Why use the Handler Constructor pattern?
Well, it depends. Take a look at the following examples:
Using the conventional pattern
func CreateProduct(w http.ResponseWriter, r *http.Request) {
var p *Product
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := db.Insert(p); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
if err := json.NewEncoder(w).Encode(p); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}Using the Handler Constructor pattern
func CreateProduct(r *http.Request) http.Handler {
var p *Product
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
return handler.Error(http.StatusBadRequest, err)
}
if err := db.Insert(p); err != nil {
return handler.Error(http.StatusInternalServerError, err)
}
return handler.JSON(http.StatusCreated, p)
}Hopefully, at least two things stick out:
- The Handler Constructor pattern allows for early function departures without requiring an empty
returnstatement. This should feel intuitive with how much we encounterreturn errstatements. - We don't even see
w http.ResponseWriterin our constructor, making it much more difficult to make the mistake of writing towwhen we shouldn't (e.g. multiple times). It is easier for this type of mistake to occur using the conventional pattern, say, forgetting an emptyreturnstatement after callinghttp.Error. Note that you can get access towin your constructor by returning a http.HandlerFunc.
When to not use the Handler Constructor pattern
The points made above are enough to convince me to use this pattern for most of my Go applications; but not all of them. Here are some reasons you may want avoid using the handler.Constructor pattern:
- You need to stream data to the
http.ResponseWriter. - You find yourself frequently needing access to
w http.ResponseWriterdirectly - to the point where the majority of your constructors become nothing more than wrappers around http.HandlerFuncs.
Routing
The Constructor type satisfies the http.Handler interface,
so they can be used anywhere a http.Handler can.
Here is an example using Handler Constructors with gorilla/mux:
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/zpatrick/handler"
)
func Index(r *http.Request) http.Handler {
return handler.String(http.StatusOK, "Hello, World!")
}
func main() {
r := mux.NewRouter()
r.Handle("/", handler.Constructor(Index))
log.Println("Listening on port 8000")
log.Fatal(http.ListenAndServe(":8000", r))
}