diff --git a/echo.go b/echo.go index 287f9fc6..7b7487fe 100644 --- a/echo.go +++ b/echo.go @@ -34,6 +34,7 @@ type ( renderer Renderer pool sync.Pool debug bool + hook http.HandlerFunc // stripTrailingSlash bool router *Router } @@ -261,6 +262,13 @@ func (e *Echo) Debug() bool { return e.debug } +// Hook registers a callback which is invoked from `Echo#ServerHTTP` as the first +// statement. Hook is useful if you want to modify response/response objects even +// before it hits the router or any middleware. +func (e *Echo) Hook(h http.HandlerFunc) { + e.hook = h +} + // StripTrailingSlash enables removing trailing slash from the request path. // func (e *Echo) StripTrailingSlash() { // e.stripTrailingSlash = true @@ -463,6 +471,10 @@ func (e *Echo) Routes() []Route { // ServeHTTP implements `http.Handler` interface, which serves HTTP requests. func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if e.hook != nil { + e.hook(w, r) + } + c := e.pool.Get().(*Context) h, e := e.router.Find(r.Method, r.URL.Path, c) c.reset(r, w, e) diff --git a/echo_test.go b/echo_test.go index 9710dd24..1f523569 100644 --- a/echo_test.go +++ b/echo_test.go @@ -406,14 +406,23 @@ func TestEchoServer(t *testing.T) { assert.IsType(t, &http.Server{}, s) } -// func TestStripTrailingSlash(t *testing.T) { -// e := New() -// e.StripTrailingSlash() -// r, _ := http.NewRequest(GET, "/users/", nil) -// w := httptest.NewRecorder() -// e.ServeHTTP(w, r) -// assert.Equal(t, http.StatusNotFound, w.Code) -// } +func TestEchoHook(t *testing.T) { + e := New() + e.Get("/test", func(c *Context) error { + return c.NoContent(http.StatusNoContent) + }) + e.Hook(func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + l := len(path) - 1 + if path != "/" && path[l] == '/' { + r.URL.Path = path[:l] + } + }) + r, _ := http.NewRequest(GET, "/test/", nil) + w := httptest.NewRecorder() + e.ServeHTTP(w, r) + assert.Equal(t, r.URL.Path, "/test") +} func testMethod(t *testing.T, method, path string, e *Echo) { m := fmt.Sprintf("%c%s", method[0], strings.ToLower(method[1:])) diff --git a/router.go b/router.go index fc2daec3..05451cd3 100644 --- a/router.go +++ b/router.go @@ -271,14 +271,6 @@ func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo e = r.echo cn := r.tree // Current node as root - // Strip trailing slash - // if r.echo.stripTrailingSlash { - // l := len(path) - 1 - // if path != "/" && path[l] == '/' { // Issue #218 - // path = path[:l] - // } - // } - var ( search = path c *node // Child node diff --git a/website/content/guide/customization.md b/website/content/guide/customization.md index 6a01aa71..782682d6 100644 --- a/website/content/guide/customization.md +++ b/website/content/guide/customization.md @@ -8,23 +8,43 @@ menu: ### HTTP error handler -`Echo.SetHTTPErrorHandler(h HTTPErrorHandler)` +`Echo#SetHTTPErrorHandler(h HTTPErrorHandler)` -Registers a custom `Echo.HTTPErrorHandler`. +Registers a custom `Echo#HTTPErrorHandler`. Default handler rules: -- If error is of type `Echo.HTTPError` it sends HTTP response with status code `HTTPError.Code` +- If error is of type `Echo#HTTPError` it sends HTTP response with status code `HTTPError.Code` and message `HTTPError.Message`. - Else it sends `500 - Internal Server Error`. - If debug mode is enabled, it uses `error.Error()` as status message. ### Debug -`Echo.SetDebug(on bool)` +`Echo#SetDebug(on bool)` Enables/disables debug mode. ### Disable colored log -`Echo.DisableColoredLog()` +`Echo#DisableColoredLog()` + +### Hook + +`Echo#Hook(http.HandlerFunc)` + +Hook registers a callback which is invoked from `Echo#ServerHTTP` as the first +statement. Hook is useful if you want to modify response/response objects even +before it hits the router or any middleware. + +For example, the following hook strips the trailing slash from the request path. + +```go +e.Hook(func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + l := len(path) - 1 + if path != "/" && path[l] == '/' { + r.URL.Path = path[:l] + } +}) +```