router: auto wrapping of http.Handler and http.HandlerFunc
diff --git a/router.go b/router.go
index f18a678..1d70c66 100644
--- a/router.go
+++ b/router.go
@@ -85,6 +85,32 @@
// wildcards (variables).
type Handle func(http.ResponseWriter, *http.Request, Params)
+// Wrap is the default wrap function to wrap handle types to Handle.
+// Currently Handle, http.Handler and http.HandlerFunc are supported.
+// Opther handle types can be used by setting a custom wrap function.
+func Wrap(handle interface{}) Handle {
+ switch h := handle.(type) {
+ case Handle:
+ return h
+ case func(http.ResponseWriter, *http.Request, Params):
+ return h
+ case http.HandlerFunc:
+ return func(w http.ResponseWriter, req *http.Request, _ Params) {
+ h(w, req)
+ }
+ case func(http.ResponseWriter, *http.Request):
+ return func(w http.ResponseWriter, req *http.Request, _ Params) {
+ h(w, req)
+ }
+ case http.Handler:
+ return func(w http.ResponseWriter, req *http.Request, _ Params) {
+ h.ServeHTTP(w, req)
+ }
+ default:
+ panic("unknown handle type")
+ }
+}
+
// Param is a single URL parameter, consisting of a key and a value.
type Param struct {
Key string
@@ -159,6 +185,11 @@
// The handler can be used to keep your server from crashing because of
// unrecovered panics.
PanicHandler func(http.ResponseWriter, *http.Request, interface{})
+
+ // Function to wrap handle types to Handle. The default function is Wrap.
+ // Custom functions can be used to allow other handle types or apply pre- /
+ // postprocessing of the request.
+ Wrap func(interface{}) Handle
}
// Make sure the Router conforms with the http.Handler interface
@@ -172,41 +203,42 @@
RedirectFixedPath: true,
HandleMethodNotAllowed: true,
HandleOPTIONS: true,
+ Wrap: Wrap,
}
}
// GET is a shortcut for router.Handle("GET", path, handle)
-func (r *Router) GET(path string, handle Handle) {
+func (r *Router) GET(path string, handle interface{}) {
r.Handle("GET", path, handle)
}
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
-func (r *Router) HEAD(path string, handle Handle) {
+func (r *Router) HEAD(path string, handle interface{}) {
r.Handle("HEAD", path, handle)
}
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
-func (r *Router) OPTIONS(path string, handle Handle) {
+func (r *Router) OPTIONS(path string, handle interface{}) {
r.Handle("OPTIONS", path, handle)
}
// POST is a shortcut for router.Handle("POST", path, handle)
-func (r *Router) POST(path string, handle Handle) {
+func (r *Router) POST(path string, handle interface{}) {
r.Handle("POST", path, handle)
}
// PUT is a shortcut for router.Handle("PUT", path, handle)
-func (r *Router) PUT(path string, handle Handle) {
+func (r *Router) PUT(path string, handle interface{}) {
r.Handle("PUT", path, handle)
}
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
-func (r *Router) PATCH(path string, handle Handle) {
+func (r *Router) PATCH(path string, handle interface{}) {
r.Handle("PATCH", path, handle)
}
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
-func (r *Router) DELETE(path string, handle Handle) {
+func (r *Router) DELETE(path string, handle interface{}) {
r.Handle("DELETE", path, handle)
}
@@ -218,11 +250,16 @@
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
-func (r *Router) Handle(method, path string, handle Handle) {
+func (r *Router) Handle(method, path string, handle interface{}) {
if path[0] != '/' {
panic("path must begin with '/' in path '" + path + "'")
}
+ if r.Wrap == nil {
+ r.Wrap = Wrap
+ }
+ h := r.Wrap(handle)
+
if r.trees == nil {
r.trees = make(map[string]*node)
}
@@ -233,23 +270,7 @@
r.trees[method] = root
}
- root.addRoute(path, handle)
-}
-
-// Handler is an adapter which allows the usage of an http.Handler as a
-// request handle.
-func (r *Router) Handler(method, path string, handler http.Handler) {
- r.Handle(method, path,
- func(w http.ResponseWriter, req *http.Request, _ Params) {
- handler.ServeHTTP(w, req)
- },
- )
-}
-
-// HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a
-// request handle.
-func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
- r.Handler(method, path, handler)
+ root.addRoute(path, h)
}
// ServeFiles serves files from the given file system root.
diff --git a/router_test.go b/router_test.go
index db57740..300fbd1 100644
--- a/router_test.go
+++ b/router_test.go
@@ -76,7 +76,8 @@
}
func TestRouterAPI(t *testing.T) {
- var get, head, options, post, put, patch, delete, handler, handlerFunc bool
+ var get, head, options, post, put, patch, delete bool
+ var handle, handler, handlerFunc1, handlerFunc2 bool
httpHandler := handlerStruct{&handler}
@@ -102,10 +103,16 @@
router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) {
delete = true
})
- router.Handler("GET", "/Handler", httpHandler)
- router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
- handlerFunc = true
+ router.Handle("GET", "/Handle", Handle(func(w http.ResponseWriter, r *http.Request, _ Params) {
+ handle = true
+ }))
+ router.Handle("GET", "/Handler", httpHandler)
+ router.Handle("GET", "/HandlerFunc1", func(w http.ResponseWriter, r *http.Request) {
+ handlerFunc1 = true
})
+ router.Handle("GET", "/HandlerFunc2", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ handlerFunc2 = true
+ }))
w := new(mockResponseWriter)
@@ -151,16 +158,57 @@
t.Error("routing DELETE failed")
}
+ r, _ = http.NewRequest("GET", "/Handle", nil)
+ router.ServeHTTP(w, r)
+ if !handle {
+ t.Error("routing Handle failed")
+ }
+
r, _ = http.NewRequest("GET", "/Handler", nil)
router.ServeHTTP(w, r)
if !handler {
t.Error("routing Handler failed")
}
- r, _ = http.NewRequest("GET", "/HandlerFunc", nil)
+ r, _ = http.NewRequest("GET", "/HandlerFunc1", nil)
router.ServeHTTP(w, r)
- if !handlerFunc {
- t.Error("routing HandlerFunc failed")
+ if !handlerFunc1 {
+ t.Error("routing HandlerFunc1 failed")
+ }
+
+ r, _ = http.NewRequest("GET", "/HandlerFunc2", nil)
+ router.ServeHTTP(w, r)
+ if !handlerFunc2 {
+ t.Error("routing HandlerFunc2 failed")
+ }
+}
+
+func TestRouterInvalidWrap(t *testing.T) {
+ r := New()
+
+ // nil handle
+ recv := catchPanic(func() {
+ r.Handle("GET", "/nil", nil)
+ })
+ if recv == nil {
+ t.Errorf("no panic when inserting nil handle")
+ }
+
+ // handle which can not be wrapped
+ recv = catchPanic(func() {
+ r.Handle("GET", "/unknown", mockResponseWriter{})
+ })
+ if recv == nil {
+ t.Errorf("no panic when inserting unknown handle")
+ }
+
+ // no wrap func. Router should fall back to default
+ r.Wrap = nil
+ recv = catchPanic(func() {
+ r.Handle("GET", "/wrap", func(w http.ResponseWriter, r *http.Request, _ Params) {})
+ })
+ if recv != nil {
+ t.Errorf("panic when inserting without wrap func")
}
}