From 5706940bc8c4c824586194caf42eb0f46658e8ee Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Sat, 10 Dec 2016 22:05:41 -0800 Subject: [PATCH] HTTPError#Message is now interface Signed-off-by: Vishal Rana --- echo.go | 83 ++++++++++++++------------ echo_test.go | 7 --- middleware/compress_test.go | 4 +- website/content/guide/customization.md | 11 ++-- 4 files changed, 54 insertions(+), 51 deletions(-) diff --git a/echo.go b/echo.go index adf43175..2f3231f4 100644 --- a/echo.go +++ b/echo.go @@ -42,7 +42,7 @@ import ( "errors" "fmt" "io" - "log" + slog "log" "net/http" "path" "reflect" @@ -51,7 +51,7 @@ import ( "time" "github.com/labstack/gommon/color" - glog "github.com/labstack/gommon/log" + "github.com/labstack/gommon/log" "github.com/tylerb/graceful" "golang.org/x/crypto/acme/autocert" ) @@ -59,23 +59,23 @@ import ( type ( // Echo is the top-level framework instance. Echo struct { - DisableHTTP2 bool - Debug bool - HTTPErrorHandler - Binder Binder - Renderer Renderer - AutoTLSManager autocert.Manager - ShutdownTimeout time.Duration - Color *color.Color - Logger Logger - server *graceful.Server - tlsServer *graceful.Server - premiddleware []MiddlewareFunc - middleware []MiddlewareFunc - maxParam *int - router *Router - notFoundHandler HandlerFunc - pool sync.Pool + DisableHTTP2 bool + Debug bool + HTTPErrorHandler HTTPErrorHandler + Binder Binder + Renderer Renderer + AutoTLSManager autocert.Manager + ShutdownTimeout time.Duration + Color *color.Color + Logger Logger + server *graceful.Server + tlsServer *graceful.Server + premiddleware []MiddlewareFunc + middleware []MiddlewareFunc + maxParam *int + router *Router + notFoundHandler HandlerFunc + pool sync.Pool } // Route contains a handler and information for matching against requests. @@ -88,7 +88,7 @@ type ( // HTTPError represents an error that occurred while handling a request. HTTPError struct { Code int - Message string + Message interface{} } // MiddlewareFunc defines a function to process middleware. @@ -240,13 +240,13 @@ func New() (e *Echo) { Prompt: autocert.AcceptTOS, }, ShutdownTimeout: 15 * time.Second, - Logger: glog.New("echo"), + Logger: log.New("echo"), maxParam: new(int), Color: color.New(), } e.HTTPErrorHandler = e.DefaultHTTPErrorHandler e.Binder = &DefaultBinder{} - e.Logger.SetLevel(glog.OFF) + e.Logger.SetLevel(log.OFF) e.pool.New = func() interface{} { return e.NewContext(nil, nil) } @@ -271,24 +271,33 @@ func (e *Echo) Router() *Router { return e.router } -// DefaultHTTPErrorHandler invokes the default HTTP error handler. +// DefaultHTTPErrorHandler is the default HTTP error handler. It sends a JSON response +// with status code. func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) { - code := http.StatusInternalServerError - msg := http.StatusText(code) + var ( + code = http.StatusInternalServerError + msg interface{} + ) + if he, ok := err.(*HTTPError); ok { code = he.Code msg = he.Message + } else { + msg = Map{"message": err} } - if e.Debug { - msg = err.Error() - } + if !c.Response().Committed { if c.Request().Method == HEAD { // Issue #608 - c.NoContent(code) + if err := c.NoContent(code); err != nil { + goto ERROR + } } else { - c.String(code, msg) + if err := c.JSON(code, msg); err != nil { + goto ERROR + } } } +ERROR: e.Logger.Error(err) } @@ -545,7 +554,7 @@ func (e *Echo) StartServer(s *http.Server) error { gs := &graceful.Server{ Server: s, Timeout: e.ShutdownTimeout, - Logger: log.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0), + Logger: slog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0), } if s.TLSConfig == nil { e.server = gs @@ -568,17 +577,17 @@ func (e *Echo) ShutdownTLS(timeout time.Duration) { } // NewHTTPError creates a new HTTPError instance. -func NewHTTPError(code int, msg ...interface{}) *HTTPError { - he := &HTTPError{Code: code, Message: http.StatusText(code)} - if len(msg) > 0 { - he.Message = fmt.Sprint(msg...) +func NewHTTPError(code int, message ...interface{}) *HTTPError { + he := &HTTPError{Code: code, Message: Map{"message": http.StatusText(code)}} + if len(message) > 0 { + he.Message = message[0] } return he } // Error makes it compatible with `error` interface. -func (e *HTTPError) Error() string { - return e.Message +func (he *HTTPError) Error() string { + return fmt.Sprintf("code=%d, message=%s", he.Code, he.Message) } // WrapHandler wraps `http.Handler` into `echo.HandlerFunc`. diff --git a/echo_test.go b/echo_test.go index 775d5e92..0e8808fd 100644 --- a/echo_test.go +++ b/echo_test.go @@ -380,13 +380,6 @@ func TestEchoMethodNotAllowed(t *testing.T) { assert.Equal(t, http.StatusMethodNotAllowed, rec.Code) } -func TestEchoHTTPError(t *testing.T) { - m := http.StatusText(http.StatusBadRequest) - he := NewHTTPError(http.StatusBadRequest, m) - assert.Equal(t, http.StatusBadRequest, he.Code) - assert.Equal(t, m, he.Error()) -} - func TestEchoContext(t *testing.T) { e := New() c := e.AcquireContext() diff --git a/middleware/compress_test.go b/middleware/compress_test.go index 68375745..08b4113c 100644 --- a/middleware/compress_test.go +++ b/middleware/compress_test.go @@ -62,11 +62,11 @@ func TestGzipErrorReturned(t *testing.T) { e := echo.New() e.Use(Gzip()) e.GET("/", func(c echo.Context) error { - return echo.NewHTTPError(http.StatusInternalServerError, "error") + return echo.ErrNotFound }) req, _ := http.NewRequest(echo.GET, "/", nil) rec := httptest.NewRecorder() e.ServeHTTP(rec, req) + assert.Equal(t, http.StatusNotFound, rec.Code) assert.Empty(t, rec.Header().Get(echo.HeaderContentEncoding)) - assert.Equal(t, "error", rec.Body.String()) } diff --git a/website/content/guide/customization.md b/website/content/guide/customization.md index 616b7137..f769ee48 100644 --- a/website/content/guide/customization.md +++ b/website/content/guide/customization.md @@ -9,14 +9,15 @@ description = "Customizing Echo" ## HTTP Error Handler -Default HTTP error handler rules: +Default HTTP error handler sends an error as JSON with the following rules: -- If error is of type `Echo#HTTPError` it sends HTTP response with status code `HTTPError.Code` +- If error is `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. +- If error is `error` it sends HTTP response with status code `500 - Internal Server Error` +and message `error.Error()`. +- It logs the error. -You can also set a custom HTTP error handler using `Echo#HTTPErrorHandler`. +You can set a custom HTTP error handler using `Echo#HTTPErrorHandler`. ## Debugging