The example implements two layers; authentication and the bottom endpoints. Each layer only needs changing for one reason. This results in simpler testing. Downside; routing is done for each layer.
func AuthLayer(next http.Handler) *http.ServeMux {
// add public patterns
mx := http.NewServeMux()
mx.Handle("GET /fruits", next)
// anything else is private and requires authentication
mx.Handle("GET /", protect(next))
return mx
}
func protect(next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// ... implement authorization here ...
w.WriteHeader(http.StatusUnauthorized)
}
}
func Endpoints() *http.ServeMux {
mx := http.NewServeMux()
mx.Handle("GET /fruits", fruits())
mx.Handle("GET /keys", keys())
return mx
}
func TestAuthLayer(t *testing.T) {
// the layerd design allows for easy replacement of lower layers
layer := AuthLayer(alwaysOK())
cases := map[string]int{
"GET /fruits": 200,
"GET /keys": 401,
}
checkResp(t, layer, cases)
}
func TestEndpoints(t *testing.T) {
layer := Endpoints()
cases := map[string]int{
"GET /fruits": 200,
"GET /keys": 200,
"POST /keys": 405,
}
checkResp(t, layer, cases)
}