1
0
mirror of https://github.com/labstack/echo.git synced 2024-12-24 20:14:31 +02:00

Merge pull request #76 from labstack/websocket

New definition for Echo.HandlerFunc
This commit is contained in:
Vishal Rana 2015-05-20 16:39:46 -07:00
commit 1e117621e9
23 changed files with 267 additions and 215 deletions

View File

@ -9,14 +9,14 @@ Echo is a fast HTTP router (zero memory allocation) and micro web framework in G
- `echo.MiddlewareFunc` - `echo.MiddlewareFunc`
- `func(echo.HandlerFunc) echo.HandlerFunc` - `func(echo.HandlerFunc) echo.HandlerFunc`
- `echo.HandlerFunc` - `echo.HandlerFunc`
- `func(*echo.Context) *echo.HTTPError` - `func(*echo.Context) error`
- `func(http.Handler) http.Handler` - `func(http.Handler) http.Handler`
- `http.Handler` - `http.Handler`
- `http.HandlerFunc` - `http.HandlerFunc`
- `func(http.ResponseWriter, *http.Request)` - `func(http.ResponseWriter, *http.Request)`
- Handler - Handler
- `echo.HandlerFunc` - `echo.HandlerFunc`
- `func(*echo.Context) *echo.HTTPError` - `func(*echo.Context) error`
- `http.Handler` - `http.Handler`
- `http.HandlerFunc` - `http.HandlerFunc`
- `func(http.ResponseWriter, *http.Request)` - `func(http.ResponseWriter, *http.Request)`
@ -84,7 +84,7 @@ import (
) )
// Handler // Handler
func hello(c *echo.Context) *echo.HTTPError { func hello(c *echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n") return c.String(http.StatusOK, "Hello, World!\n")
} }
@ -133,34 +133,34 @@ var (
// Handlers // Handlers
//---------- //----------
func createUser(c *echo.Context) *echo.HTTPError { func createUser(c *echo.Context) error {
u := &user{ u := &user{
ID: seq, ID: seq,
} }
if he := c.Bind(u); he != nil { if err := c.Bind(u); err != nil {
return he return err
} }
users[u.ID] = u users[u.ID] = u
seq++ seq++
return c.JSON(http.StatusCreated, u) return c.JSON(http.StatusCreated, u)
} }
func getUser(c *echo.Context) *echo.HTTPError { func getUser(c *echo.Context) error {
id, _ := strconv.Atoi(c.Param("id")) id, _ := strconv.Atoi(c.Param("id"))
return c.JSON(http.StatusOK, users[id]) return c.JSON(http.StatusOK, users[id])
} }
func updateUser(c *echo.Context) *echo.HTTPError { func updateUser(c *echo.Context) error {
u := new(user) u := new(user)
if he := c.Bind(u); he != nil { if err := c.Bind(u); err != nil {
return he return err
} }
id, _ := strconv.Atoi(c.Param("id")) id, _ := strconv.Atoi(c.Param("id"))
users[id].Name = u.Name users[id].Name = u.Name
return c.JSON(http.StatusOK, users[id]) return c.JSON(http.StatusOK, users[id])
} }
func deleteUser(c *echo.Context) *echo.HTTPError { func deleteUser(c *echo.Context) error {
id, _ := strconv.Atoi(c.Param("id")) id, _ := strconv.Atoi(c.Param("id"))
delete(users, id) delete(users, id)
return c.NoContent(http.StatusNoContent) return c.NoContent(http.StatusNoContent)
@ -218,22 +218,19 @@ var (
) )
// Render HTML // Render HTML
func (t *Template) Render(w io.Writer, name string, data interface{}) *echo.HTTPError { func (t *Template) Render(w io.Writer, name string, data interface{}) error {
if err := t.templates.ExecuteTemplate(w, name, data); err != nil { return t.templates.ExecuteTemplate(w, name, data)
return &echo.HTTPError{Error: err}
}
return nil
} }
//---------- //----------
// Handlers // Handlers
//---------- //----------
func welcome(c *echo.Context) *echo.HTTPError { func welcome(c *echo.Context) error {
return c.Render(http.StatusOK, "welcome", "Joe") return c.Render(http.StatusOK, "welcome", "Joe")
} }
func createUser(c *echo.Context) *echo.HTTPError { func createUser(c *echo.Context) error {
u := new(user) u := new(user)
if err := c.Bind(u); err != nil { if err := c.Bind(u); err != nil {
return err return err
@ -242,11 +239,11 @@ func createUser(c *echo.Context) *echo.HTTPError {
return c.JSON(http.StatusCreated, u) return c.JSON(http.StatusCreated, u)
} }
func getUsers(c *echo.Context) *echo.HTTPError { func getUsers(c *echo.Context) error {
return c.JSON(http.StatusOK, users) return c.JSON(http.StatusOK, users)
} }
func getUser(c *echo.Context) *echo.HTTPError { func getUser(c *echo.Context) error {
return c.JSON(http.StatusOK, users[c.P(0)]) return c.JSON(http.StatusOK, users[c.P(0)])
} }
@ -268,7 +265,7 @@ func main() {
s := stats.New() s := stats.New()
e.Use(s.Handler) e.Use(s.Handler)
// Route // Route
e.Get("/stats", func(c *echo.Context) *echo.HTTPError { e.Get("/stats", func(c *echo.Context) error {
return c.JSON(http.StatusOK, s.Data()) return c.JSON(http.StatusOK, s.Data())
}) })
@ -297,7 +294,7 @@ func main() {
// Cached templates // Cached templates
templates: template.Must(template.ParseFiles("public/views/welcome.html")), templates: template.Must(template.ParseFiles("public/views/welcome.html")),
} }
e.Renderer(t) e.SetRenderer(t)
e.Get("/welcome", welcome) e.Get("/welcome", welcome)
//------- //-------
@ -306,20 +303,20 @@ func main() {
// Group with parent middleware // Group with parent middleware
a := e.Group("/admin") a := e.Group("/admin")
a.Use(func(c *echo.Context) *echo.HTTPError { a.Use(func(c *echo.Context) error {
// Security middleware // Security middleware
return nil return nil
}) })
a.Get("", func(c *echo.Context) *echo.HTTPError { a.Get("", func(c *echo.Context) error {
return c.String(http.StatusOK, "Welcome admin!") return c.String(http.StatusOK, "Welcome admin!")
}) })
// Group with no parent middleware // Group with no parent middleware
g := e.Group("/files", func(c *echo.Context) *echo.HTTPError { g := e.Group("/files", func(c *echo.Context) error {
// Security middleware // Security middleware
return nil return nil
}) })
g.Get("", func(c *echo.Context) *echo.HTTPError { g.Get("", func(c *echo.Context) error {
return c.String(http.StatusOK, "Your files!") return c.String(http.StatusOK, "Your files!")
}) })
@ -350,7 +347,7 @@ import (
) )
// Handler // Handler
func hello(c *echo.Context) *echo.HTTPError { func hello(c *echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n") return c.String(http.StatusOK, "Hello, World!\n")
} }
@ -359,7 +356,7 @@ func main() {
e := echo.New() e := echo.New()
// Debug mode // Debug mode
e.Debug(true) e.SetDebug(true)
//------------ //------------
// Middleware // Middleware

View File

@ -3,6 +3,8 @@ package echo
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"golang.org/x/net/websocket"
) )
type ( type (
@ -11,6 +13,7 @@ type (
Context struct { Context struct {
Request *http.Request Request *http.Request
Response *Response Response *Response
Socket *websocket.Conn
pnames []string pnames []string
pvalues []string pvalues []string
store store store store
@ -53,15 +56,15 @@ func (c *Context) Param(name string) (value string) {
// Bind binds the request body into specified type v. Default binder does it // Bind binds the request body into specified type v. Default binder does it
// based on Content-Type header. // based on Content-Type header.
func (c *Context) Bind(i interface{}) *HTTPError { func (c *Context) Bind(i interface{}) error {
return c.echo.binder(c.Request, i) return c.echo.binder(c.Request, i)
} }
// Render invokes the registered HTML template renderer and sends a text/html // Render invokes the registered HTML template renderer and sends a text/html
// response with status code. // response with status code.
func (c *Context) Render(code int, name string, data interface{}) *HTTPError { func (c *Context) Render(code int, name string, data interface{}) error {
if c.echo.renderer == nil { if c.echo.renderer == nil {
return &HTTPError{Error: RendererNotRegistered} return RendererNotRegistered
} }
c.Response.Header().Set(ContentType, TextHTML+"; charset=utf-8") c.Response.Header().Set(ContentType, TextHTML+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
@ -69,44 +72,37 @@ func (c *Context) Render(code int, name string, data interface{}) *HTTPError {
} }
// JSON sends an application/json response with status code. // JSON sends an application/json response with status code.
func (c *Context) JSON(code int, i interface{}) *HTTPError { func (c *Context) JSON(code int, i interface{}) error {
c.Response.Header().Set(ContentType, ApplicationJSON+"; charset=utf-8") c.Response.Header().Set(ContentType, ApplicationJSON+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
if err := json.NewEncoder(c.Response).Encode(i); err != nil { return json.NewEncoder(c.Response).Encode(i)
return &HTTPError{Error: err}
}
return nil
} }
// String sends a text/plain response with status code. // String sends a text/plain response with status code.
func (c *Context) String(code int, s string) *HTTPError { func (c *Context) String(code int, s string) error {
c.Response.Header().Set(ContentType, TextPlain+"; charset=utf-8") c.Response.Header().Set(ContentType, TextPlain+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
if _, err := c.Response.Write([]byte(s)); err != nil { _, err := c.Response.Write([]byte(s))
return &HTTPError{Error: err} return err
}
return nil
} }
// HTML sends a text/html response with status code. // HTML sends a text/html response with status code.
func (c *Context) HTML(code int, html string) *HTTPError { func (c *Context) HTML(code int, html string) error {
c.Response.Header().Set(ContentType, TextHTML+"; charset=utf-8") c.Response.Header().Set(ContentType, TextHTML+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
if _, err := c.Response.Write([]byte(html)); err != nil { _, err := c.Response.Write([]byte(html))
return &HTTPError{Error: err} return err
}
return nil
} }
// NoContent sends a response with no body and a status code. // NoContent sends a response with no body and a status code.
func (c *Context) NoContent(code int) *HTTPError { func (c *Context) NoContent(code int) error {
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
return nil return nil
} }
// Error invokes the registered HTTP error handler. // Error invokes the registered HTTP error handler.
func (c *Context) Error(he *HTTPError) { func (c *Context) Error(err error) {
c.echo.httpErrorHandler(he, c) c.echo.httpErrorHandler(err, c)
} }
// Get retrieves data from the context. // Get retrieves data from the context.

View File

@ -16,17 +16,14 @@ type (
} }
) )
func (t *Template) Render(w io.Writer, name string, data interface{}) *HTTPError { func (t *Template) Render(w io.Writer, name string, data interface{}) error {
if err := t.templates.ExecuteTemplate(w, name, data); err != nil { return t.templates.ExecuteTemplate(w, name, data)
return &HTTPError{Error: err}
}
return nil
} }
func TestContext(t *testing.T) { func TestContext(t *testing.T) {
b, _ := json.Marshal(u1) b, _ := json.Marshal(u1)
r, _ := http.NewRequest(POST, "/users/1", bytes.NewReader(b)) r, _ := http.NewRequest(POST, "/users/1", bytes.NewReader(b))
c := NewContext(r, &Response{Writer: httptest.NewRecorder()}, New()) c := NewContext(r, NewResponse(httptest.NewRecorder()), New())
//------ //------
// Bind // Bind

96
echo.go
View File

@ -14,6 +14,7 @@ import (
"sync" "sync"
"github.com/mattn/go-colorable" "github.com/mattn/go-colorable"
"golang.org/x/net/websocket"
) )
type ( type (
@ -33,24 +34,23 @@ type (
HTTPError struct { HTTPError struct {
Code int Code int
Message string Message string
Error error
} }
Middleware interface{} Middleware interface{}
MiddlewareFunc func(HandlerFunc) HandlerFunc MiddlewareFunc func(HandlerFunc) HandlerFunc
Handler interface{} Handler interface{}
HandlerFunc func(*Context) *HTTPError HandlerFunc func(*Context) error
// HTTPErrorHandler is a centralized HTTP error handler. // HTTPErrorHandler is a centralized HTTP error handler.
HTTPErrorHandler func(*HTTPError, *Context) HTTPErrorHandler func(error, *Context)
BindFunc func(*http.Request, interface{}) *HTTPError BindFunc func(*http.Request, interface{}) error
// Renderer is the interface that wraps the Render method. // Renderer is the interface that wraps the Render method.
// //
// Render renders the HTML template with given name and specified data. // Render renders the HTML template with given name and specified data.
// It writes the output to w. // It writes the output to w.
Renderer interface { Renderer interface {
Render(w io.Writer, name string, data interface{}) *HTTPError Render(w io.Writer, name string, data interface{}) error
} }
) )
@ -120,6 +120,18 @@ var (
RendererNotRegistered = errors.New("echo ⇒ renderer not registered") RendererNotRegistered = errors.New("echo ⇒ renderer not registered")
) )
func NewHTTPError(code int, msgs ...string) *HTTPError {
he := &HTTPError{Code: code}
if len(msgs) == 0 {
he.Message = http.StatusText(code)
}
return he
}
func (e *HTTPError) Error() string {
return e.Message
}
// New creates an Echo instance. // New creates an Echo instance.
func New() (e *Echo) { func New() (e *Echo) {
e = &Echo{ e = &Echo{
@ -135,22 +147,22 @@ func New() (e *Echo) {
//---------- //----------
e.SetMaxParam(5) e.SetMaxParam(5)
e.notFoundHandler = func(c *Context) *HTTPError { e.notFoundHandler = func(c *Context) error {
return &HTTPError{Code: http.StatusNotFound} return NewHTTPError(http.StatusNotFound)
} }
e.SetHTTPErrorHandler(func(he *HTTPError, c *Context) { e.SetHTTPErrorHandler(func(err error, c *Context) {
if he.Code == 0 { code := http.StatusInternalServerError
he.Code = http.StatusInternalServerError msg := http.StatusText(code)
if he, ok := err.(*HTTPError); ok {
code = he.Code
msg = he.Message
} }
if he.Message == "" { if e.Debug() {
he.Message = http.StatusText(he.Code) msg = err.Error()
} }
if e.debug && he.Error != nil { http.Error(c.Response, msg, code)
he.Message = he.Error.Error()
}
http.Error(c.Response, he.Message, he.Code)
}) })
e.SetBinder(func(r *http.Request, v interface{}) *HTTPError { e.SetBinder(func(r *http.Request, v interface{}) error {
ct := r.Header.Get(ContentType) ct := r.Header.Get(ContentType)
err := UnsupportedMediaType err := UnsupportedMediaType
if strings.HasPrefix(ct, ApplicationJSON) { if strings.HasPrefix(ct, ApplicationJSON) {
@ -158,10 +170,7 @@ func New() (e *Echo) {
} else if strings.HasPrefix(ct, ApplicationForm) { } else if strings.HasPrefix(ct, ApplicationForm) {
err = nil err = nil
} }
if err != nil { return err
return &HTTPError{Error: err}
}
return nil
}) })
return return
} }
@ -261,6 +270,21 @@ func (e *Echo) Trace(path string, h Handler) {
e.add(TRACE, path, h) e.add(TRACE, path, h)
} }
// WebSocket adds a WebSocket route > handler to the router.
func (e *Echo) WebSocket(path string, h HandlerFunc) {
e.Get(path, func(c *Context) *HTTPError {
wss := websocket.Server{
Handler: func(ws *websocket.Conn) {
c.Socket = ws
c.Response.status = http.StatusSwitchingProtocols
h(c)
},
}
wss.ServeHTTP(c.Response.writer, c.Request)
return nil
})
}
func (e *Echo) add(method, path string, h Handler) { func (e *Echo) add(method, path string, h Handler) {
key := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name() key := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
e.uris[key] = path e.uris[key] = path
@ -280,7 +304,7 @@ func (e *Echo) Favicon(file string) {
// Static serves static files. // Static serves static files.
func (e *Echo) Static(path, root string) { func (e *Echo) Static(path, root string) {
fs := http.StripPrefix(path, http.FileServer(http.Dir(root))) fs := http.StripPrefix(path, http.FileServer(http.Dir(root)))
e.Get(path+"*", func(c *Context) *HTTPError { e.Get(path+"*", func(c *Context) error {
fs.ServeHTTP(c.Response, c.Request) fs.ServeHTTP(c.Response, c.Request)
return nil return nil
}) })
@ -288,7 +312,7 @@ func (e *Echo) Static(path, root string) {
// ServeFile serves a file. // ServeFile serves a file.
func (e *Echo) ServeFile(path, file string) { func (e *Echo) ServeFile(path, file string) {
e.Get(path, func(c *Context) *HTTPError { e.Get(path, func(c *Context) error {
http.ServeFile(c.Response, c.Request, file) http.ServeFile(c.Response, c.Request, file)
return nil return nil
}) })
@ -376,16 +400,16 @@ func wrapMiddleware(m Middleware) MiddlewareFunc {
return m return m
case HandlerFunc: case HandlerFunc:
return wrapHandlerFuncMW(m) return wrapHandlerFuncMW(m)
case func(*Context) *HTTPError: case func(*Context) error:
return wrapHandlerFuncMW(m) return wrapHandlerFuncMW(m)
case func(http.Handler) http.Handler: case func(http.Handler) http.Handler:
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) HandlerFunc {
return func(c *Context) (he *HTTPError) { return func(c *Context) (err error) {
m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Response.Writer = w c.Response.writer = w
c.Request = r c.Request = r
he = h(c) err = h(c)
})).ServeHTTP(c.Response.Writer, c.Request) })).ServeHTTP(c.Response.writer, c.Request)
return return
} }
} }
@ -403,9 +427,9 @@ func wrapMiddleware(m Middleware) MiddlewareFunc {
// Wraps HandlerFunc middleware // Wraps HandlerFunc middleware
func wrapHandlerFuncMW(m HandlerFunc) MiddlewareFunc { func wrapHandlerFuncMW(m HandlerFunc) MiddlewareFunc {
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) HandlerFunc {
return func(c *Context) *HTTPError { return func(c *Context) error {
if he := m(c); he != nil { if err := m(c); err != nil {
return he return err
} }
return h(c) return h(c)
} }
@ -415,9 +439,9 @@ func wrapHandlerFuncMW(m HandlerFunc) MiddlewareFunc {
// Wraps http.HandlerFunc middleware // Wraps http.HandlerFunc middleware
func wrapHTTPHandlerFuncMW(m http.HandlerFunc) MiddlewareFunc { func wrapHTTPHandlerFuncMW(m http.HandlerFunc) MiddlewareFunc {
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) HandlerFunc {
return func(c *Context) *HTTPError { return func(c *Context) error {
if !c.Response.committed { if !c.Response.committed {
m.ServeHTTP(c.Response.Writer, c.Request) m.ServeHTTP(c.Response.writer, c.Request)
} }
return h(c) return h(c)
} }
@ -429,15 +453,15 @@ func wrapHandler(h Handler) HandlerFunc {
switch h := h.(type) { switch h := h.(type) {
case HandlerFunc: case HandlerFunc:
return h return h
case func(*Context) *HTTPError: case func(*Context) error:
return h return h
case http.Handler, http.HandlerFunc: case http.Handler, http.HandlerFunc:
return func(c *Context) *HTTPError { return func(c *Context) error {
h.(http.Handler).ServeHTTP(c.Response, c.Request) h.(http.Handler).ServeHTTP(c.Response, c.Request)
return nil return nil
} }
case func(http.ResponseWriter, *http.Request): case func(http.ResponseWriter, *http.Request):
return func(c *Context) *HTTPError { return func(c *Context) error {
h(c.Response, c.Request) h(c.Response, c.Request)
return nil return nil
} }

View File

@ -67,7 +67,7 @@ func TestEchoMiddleware(t *testing.T) {
// MiddlewareFunc // MiddlewareFunc
e.Use(MiddlewareFunc(func(h HandlerFunc) HandlerFunc { e.Use(MiddlewareFunc(func(h HandlerFunc) HandlerFunc {
return func(c *Context) *HTTPError { return func(c *Context) error {
b.WriteString("a") b.WriteString("a")
return h(c) return h(c)
} }
@ -75,14 +75,14 @@ func TestEchoMiddleware(t *testing.T) {
// func(echo.HandlerFunc) echo.HandlerFunc // func(echo.HandlerFunc) echo.HandlerFunc
e.Use(func(h HandlerFunc) HandlerFunc { e.Use(func(h HandlerFunc) HandlerFunc {
return func(c *Context) *HTTPError { return func(c *Context) error {
b.WriteString("b") b.WriteString("b")
return h(c) return h(c)
} }
}) })
// func(*echo.Context) *HTTPError // func(*echo.Context) error
e.Use(func(c *Context) *HTTPError { e.Use(func(c *Context) error {
b.WriteString("c") b.WriteString("c")
return nil return nil
}) })
@ -111,7 +111,7 @@ func TestEchoMiddleware(t *testing.T) {
}) })
// Route // Route
e.Get("/hello", func(c *Context) *HTTPError { e.Get("/hello", func(c *Context) error {
return c.String(http.StatusOK, "world") return c.String(http.StatusOK, "world")
}) })
@ -130,7 +130,7 @@ func TestEchoHandler(t *testing.T) {
e := New() e := New()
// HandlerFunc // HandlerFunc
e.Get("/1", HandlerFunc(func(c *Context) *HTTPError { e.Get("/1", HandlerFunc(func(c *Context) error {
return c.String(http.StatusOK, "1") return c.String(http.StatusOK, "1")
})) }))
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -140,8 +140,8 @@ func TestEchoHandler(t *testing.T) {
t.Error("body should be 1") t.Error("body should be 1")
} }
// func(*echo.Context) *HTTPError // func(*echo.Context) error
e.Get("/2", func(c *Context) *HTTPError { e.Get("/2", func(c *Context) error {
return c.String(http.StatusOK, "2") return c.String(http.StatusOK, "2")
}) })
w = httptest.NewRecorder() w = httptest.NewRecorder()
@ -177,11 +177,11 @@ func TestEchoHandler(t *testing.T) {
func TestEchoGroup(t *testing.T) { func TestEchoGroup(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
e := New() e := New()
e.Use(func(*Context) *HTTPError { e.Use(func(*Context) error {
b.WriteString("1") b.WriteString("1")
return nil return nil
}) })
e.Get("/users", func(*Context) *HTTPError { return nil }) e.Get("/users", func(*Context) error { return nil })
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, _ := http.NewRequest(GET, "/users", nil) r, _ := http.NewRequest(GET, "/users", nil)
e.ServeHTTP(w, r) e.ServeHTTP(w, r)
@ -191,11 +191,11 @@ func TestEchoGroup(t *testing.T) {
// Group // Group
g1 := e.Group("/group1") g1 := e.Group("/group1")
g1.Use(func(*Context) *HTTPError { g1.Use(func(*Context) error {
b.WriteString("2") b.WriteString("2")
return nil return nil
}) })
g1.Get("/home", func(*Context) *HTTPError { return nil }) g1.Get("/home", func(*Context) error { return nil })
b.Reset() b.Reset()
w = httptest.NewRecorder() w = httptest.NewRecorder()
r, _ = http.NewRequest(GET, "/group1/home", nil) r, _ = http.NewRequest(GET, "/group1/home", nil)
@ -205,11 +205,11 @@ func TestEchoGroup(t *testing.T) {
} }
// Group with no parent middleware // Group with no parent middleware
g2 := e.Group("/group2", func(*Context) *HTTPError { g2 := e.Group("/group2", func(*Context) error {
b.WriteString("3") b.WriteString("3")
return nil return nil
}) })
g2.Get("/home", func(*Context) *HTTPError { return nil }) g2.Get("/home", func(*Context) error { return nil })
b.Reset() b.Reset()
w = httptest.NewRecorder() w = httptest.NewRecorder()
r, _ = http.NewRequest(GET, "/group2/home", nil) r, _ = http.NewRequest(GET, "/group2/home", nil)
@ -221,7 +221,7 @@ func TestEchoGroup(t *testing.T) {
// Nested group // Nested group
g3 := e.Group("/group3") g3 := e.Group("/group3")
g4 := g3.Group("/group4") g4 := g3.Group("/group4")
g4.Get("/home", func(c *Context) *HTTPError { g4.Get("/home", func(c *Context) error {
return c.NoContent(http.StatusOK) return c.NoContent(http.StatusOK)
}) })
w = httptest.NewRecorder() w = httptest.NewRecorder()
@ -234,7 +234,7 @@ func TestEchoGroup(t *testing.T) {
func TestEchoMethod(t *testing.T) { func TestEchoMethod(t *testing.T) {
e := New() e := New()
h := func(*Context) *HTTPError { return nil } h := func(*Context) error { return nil }
e.Connect("/", h) e.Connect("/", h)
e.Delete("/", h) e.Delete("/", h)
e.Get("/", h) e.Get("/", h)
@ -248,9 +248,9 @@ func TestEchoMethod(t *testing.T) {
func TestEchoURL(t *testing.T) { func TestEchoURL(t *testing.T) {
e := New() e := New()
static := func(*Context) *HTTPError { return nil } static := func(*Context) error { return nil }
getUser := func(*Context) *HTTPError { return nil } getUser := func(*Context) error { return nil }
getFile := func(*Context) *HTTPError { return nil } getFile := func(*Context) error { return nil }
e.Get("/static/file", static) e.Get("/static/file", static)
e.Get("/users/:id", getUser) e.Get("/users/:id", getUser)
e.Get("/users/:uid/files/:fid", getFile) e.Get("/users/:uid/files/:fid", getFile)

View File

@ -24,34 +24,34 @@ var (
// Handlers // Handlers
//---------- //----------
func createUser(c *echo.Context) *echo.HTTPError { func createUser(c *echo.Context) error {
u := &user{ u := &user{
ID: seq, ID: seq,
} }
if he := c.Bind(u); he != nil { if err := c.Bind(u); err != nil {
return he return err
} }
users[u.ID] = u users[u.ID] = u
seq++ seq++
return c.JSON(http.StatusCreated, u) return c.JSON(http.StatusCreated, u)
} }
func getUser(c *echo.Context) *echo.HTTPError { func getUser(c *echo.Context) error {
id, _ := strconv.Atoi(c.Param("id")) id, _ := strconv.Atoi(c.Param("id"))
return c.JSON(http.StatusOK, users[id]) return c.JSON(http.StatusOK, users[id])
} }
func updateUser(c *echo.Context) *echo.HTTPError { func updateUser(c *echo.Context) error {
u := new(user) u := new(user)
if he := c.Bind(u); he != nil { if err := c.Bind(u); err != nil {
return he return err
} }
id, _ := strconv.Atoi(c.Param("id")) id, _ := strconv.Atoi(c.Param("id"))
users[id].Name = u.Name users[id].Name = u.Name
return c.JSON(http.StatusOK, users[id]) return c.JSON(http.StatusOK, users[id])
} }
func deleteUser(c *echo.Context) *echo.HTTPError { func deleteUser(c *echo.Context) error {
id, _ := strconv.Atoi(c.Param("id")) id, _ := strconv.Atoi(c.Param("id"))
delete(users, id) delete(users, id)
return c.NoContent(http.StatusNoContent) return c.NoContent(http.StatusNoContent)

View File

@ -8,7 +8,7 @@ import (
) )
// Handler // Handler
func hello(c *echo.Context) *echo.HTTPError { func hello(c *echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n") return c.String(http.StatusOK, "Hello, World!\n")
} }

View File

@ -8,7 +8,7 @@ import (
) )
// Handler // Handler
func hello(c *echo.Context) *echo.HTTPError { func hello(c *echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n") return c.String(http.StatusOK, "Hello, World!\n")
} }

View File

@ -29,22 +29,19 @@ var (
) )
// Render HTML // Render HTML
func (t *Template) Render(w io.Writer, name string, data interface{}) *echo.HTTPError { func (t *Template) Render(w io.Writer, name string, data interface{}) error {
if err := t.templates.ExecuteTemplate(w, name, data); err != nil { return t.templates.ExecuteTemplate(w, name, data)
return &echo.HTTPError{Error: err}
}
return nil
} }
//---------- //----------
// Handlers // Handlers
//---------- //----------
func welcome(c *echo.Context) *echo.HTTPError { func welcome(c *echo.Context) error {
return c.Render(http.StatusOK, "welcome", "Joe") return c.Render(http.StatusOK, "welcome", "Joe")
} }
func createUser(c *echo.Context) *echo.HTTPError { func createUser(c *echo.Context) error {
u := new(user) u := new(user)
if err := c.Bind(u); err != nil { if err := c.Bind(u); err != nil {
return err return err
@ -53,11 +50,11 @@ func createUser(c *echo.Context) *echo.HTTPError {
return c.JSON(http.StatusCreated, u) return c.JSON(http.StatusCreated, u)
} }
func getUsers(c *echo.Context) *echo.HTTPError { func getUsers(c *echo.Context) error {
return c.JSON(http.StatusOK, users) return c.JSON(http.StatusOK, users)
} }
func getUser(c *echo.Context) *echo.HTTPError { func getUser(c *echo.Context) error {
return c.JSON(http.StatusOK, users[c.P(0)]) return c.JSON(http.StatusOK, users[c.P(0)])
} }
@ -79,7 +76,7 @@ func main() {
s := stats.New() s := stats.New()
e.Use(s.Handler) e.Use(s.Handler)
// Route // Route
e.Get("/stats", func(c *echo.Context) *echo.HTTPError { e.Get("/stats", func(c *echo.Context) error {
return c.JSON(http.StatusOK, s.Data()) return c.JSON(http.StatusOK, s.Data())
}) })
@ -117,20 +114,20 @@ func main() {
// Group with parent middleware // Group with parent middleware
a := e.Group("/admin") a := e.Group("/admin")
a.Use(func(c *echo.Context) *echo.HTTPError { a.Use(func(c *echo.Context) error {
// Security middleware // Security middleware
return nil return nil
}) })
a.Get("", func(c *echo.Context) *echo.HTTPError { a.Get("", func(c *echo.Context) error {
return c.String(http.StatusOK, "Welcome admin!") return c.String(http.StatusOK, "Welcome admin!")
}) })
// Group with no parent middleware // Group with no parent middleware
g := e.Group("/files", func(c *echo.Context) *echo.HTTPError { g := e.Group("/files", func(c *echo.Context) error {
// Security middleware // Security middleware
return nil return nil
}) })
g.Get("", func(c *echo.Context) *echo.HTTPError { g.Get("", func(c *echo.Context) error {
return c.String(http.StatusOK, "Your files!") return c.String(http.StatusOK, "Your files!")
}) })

View File

@ -16,10 +16,10 @@ const (
// BasicAuth returns an HTTP basic authentication middleware. // BasicAuth returns an HTTP basic authentication middleware.
func BasicAuth(fn AuthFunc) echo.HandlerFunc { func BasicAuth(fn AuthFunc) echo.HandlerFunc {
return func(c *echo.Context) (he *echo.HTTPError) { return func(c *echo.Context) error {
auth := c.Request.Header.Get(echo.Authorization) auth := c.Request.Header.Get(echo.Authorization)
i := 0 i := 0
he = &echo.HTTPError{Code: http.StatusUnauthorized} he := echo.NewHTTPError(http.StatusUnauthorized)
for ; i < len(auth); i++ { for ; i < len(auth); i++ {
c := auth[i] c := auth[i]
@ -33,31 +33,31 @@ func BasicAuth(fn AuthFunc) echo.HandlerFunc {
// Ignore case // Ignore case
if i == 0 { if i == 0 {
if c != Basic[i] && c != 'b' { if c != Basic[i] && c != 'b' {
return return he
} }
} else { } else {
if c != Basic[i] { if c != Basic[i] {
return return he
} }
} }
} else { } else {
// Extract credentials // Extract credentials
b, err := base64.StdEncoding.DecodeString(auth[i:]) b, err := base64.StdEncoding.DecodeString(auth[i:])
if err != nil { if err != nil {
return return he
} }
cred := string(b) cred := string(b)
for i := 0; i < len(cred); i++ { for i := 0; i < len(cred); i++ {
if cred[i] == ':' { if cred[i] == ':' {
// Verify credentials // Verify credentials
if !fn(cred[:i], cred[i+1:]) { if !fn(cred[:i], cred[i+1:]) {
return return he
} }
return nil return nil
} }
} }
} }
} }
return return he
} }
} }

View File

@ -25,13 +25,13 @@ func Gzip() echo.MiddlewareFunc {
scheme := "gzip" scheme := "gzip"
return func(h echo.HandlerFunc) echo.HandlerFunc { return func(h echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) *echo.HTTPError { return func(c *echo.Context) error {
if strings.Contains(c.Request.Header.Get(echo.AcceptEncoding), scheme) { if strings.Contains(c.Request.Header.Get(echo.AcceptEncoding), scheme) {
w := gzip.NewWriter(c.Response.Writer) w := gzip.NewWriter(c.Response.Writer())
defer w.Close() defer w.Close()
gw := gzipWriter{Writer: w, Response: c.Response} gw := gzipWriter{Writer: w, Response: c.Response}
c.Response.Header().Set(echo.ContentEncoding, scheme) c.Response.Header().Set(echo.ContentEncoding, scheme)
c.Response = &echo.Response{Writer: gw} c.Response = echo.NewResponse(gw)
} }
return h(c) return h(c)
} }

View File

@ -14,9 +14,9 @@ func TestGzip(t *testing.T) {
// Empty Accept-Encoding header // Empty Accept-Encoding header
req, _ := http.NewRequest(echo.GET, "/", nil) req, _ := http.NewRequest(echo.GET, "/", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
res := &echo.Response{Writer: w} res := echo.NewResponse(w)
c := echo.NewContext(req, res, echo.New()) c := echo.NewContext(req, res, echo.New())
h := func(c *echo.Context) *echo.HTTPError { h := func(c *echo.Context) error {
return c.String(http.StatusOK, "test") return c.String(http.StatusOK, "test")
} }
Gzip()(h)(c) Gzip()(h)(c)
@ -28,7 +28,7 @@ func TestGzip(t *testing.T) {
// Content-Encoding header // Content-Encoding header
req.Header.Set(echo.AcceptEncoding, "gzip") req.Header.Set(echo.AcceptEncoding, "gzip")
w = httptest.NewRecorder() w = httptest.NewRecorder()
c.Response = &echo.Response{Writer: w} c.Response = echo.NewResponse(w)
Gzip()(h)(c) Gzip()(h)(c)
ce := w.Header().Get(echo.ContentEncoding) ce := w.Header().Get(echo.ContentEncoding)
if ce != "gzip" { if ce != "gzip" {

View File

@ -10,10 +10,10 @@ import (
func Logger() echo.MiddlewareFunc { func Logger() echo.MiddlewareFunc {
return func(h echo.HandlerFunc) echo.HandlerFunc { return func(h echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) *echo.HTTPError { return func(c *echo.Context) error {
start := time.Now() start := time.Now()
if he := h(c); he != nil { if err := h(c); err != nil {
c.Error(he) c.Error(err)
} }
end := time.Now() end := time.Now()
method := c.Request.Method method := c.Request.Method

View File

@ -1 +1,36 @@
package middleware package middleware
import (
"github.com/labstack/echo"
"net/http"
"net/http/httptest"
"testing"
)
func TestLogger(t *testing.T) {
e := echo.New()
req, _ := http.NewRequest(echo.GET, "/", nil)
w := httptest.NewRecorder()
res := echo.NewResponse(w)
c := echo.NewContext(req, res, e)
// Status 2xx
h := func(c *echo.Context) error {
return c.String(http.StatusOK, "test")
}
Logger()(h)(c)
// Status 4xx
c.Response = echo.NewResponse(w)
h = func(c *echo.Context) error {
return c.String(http.StatusNotFound, "test")
}
Logger()(h)(c)
// Status 5xx
c.Response = echo.NewResponse(w)
h = func(c *echo.Context) error {
return c.String(http.StatusInternalServerError, "test")
}
Logger()(h)(c)
}

View File

@ -13,15 +13,13 @@ import (
func Recover() echo.MiddlewareFunc { func Recover() echo.MiddlewareFunc {
// TODO: Provide better stack trace `https://github.com/go-errors/errors` `https://github.com/docker/libcontainer/tree/master/stacktrace` // TODO: Provide better stack trace `https://github.com/go-errors/errors` `https://github.com/docker/libcontainer/tree/master/stacktrace`
return func(h echo.HandlerFunc) echo.HandlerFunc { return func(h echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) *echo.HTTPError { return func(c *echo.Context) error {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
trace := make([]byte, 1<<16) trace := make([]byte, 1<<16)
n := runtime.Stack(trace, true) n := runtime.Stack(trace, true)
c.Error(&echo.HTTPError{ c.Error(fmt.Errorf("echo => panic recover\n %v\n stack trace %d bytes\n %s",
Error: fmt.Errorf("echo => panic recover\n %v\n stack trace %d bytes\n %s", err, n, trace[:n]))
err, n, trace[:n]),
})
} }
}() }()
return h(c) return h(c)

View File

@ -13,9 +13,9 @@ func TestRecover(t *testing.T) {
e.SetDebug(true) e.SetDebug(true)
req, _ := http.NewRequest(echo.GET, "/", nil) req, _ := http.NewRequest(echo.GET, "/", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
res := &echo.Response{Writer: w} res := echo.NewResponse(w)
c := echo.NewContext(req, res, e) c := echo.NewContext(req, res, e)
h := func(c *echo.Context) *echo.HTTPError { h := func(c *echo.Context) error {
panic("test") panic("test")
} }

View File

@ -14,7 +14,7 @@ type (
// StripTrailingSlash returns a middleware which removes trailing slash from request // StripTrailingSlash returns a middleware which removes trailing slash from request
// path. // path.
func StripTrailingSlash() echo.HandlerFunc { func StripTrailingSlash() echo.HandlerFunc {
return func(c *echo.Context) *echo.HTTPError { return func(c *echo.Context) error {
p := c.Request.URL.Path p := c.Request.URL.Path
l := len(p) l := len(p)
if p[l-1] == '/' { if p[l-1] == '/' {
@ -35,7 +35,7 @@ func RedirectToSlash(opts ...RedirectToSlashOptions) echo.HandlerFunc {
} }
} }
return func(c *echo.Context) (he *echo.HTTPError) { return func(c *echo.Context) error {
p := c.Request.URL.Path p := c.Request.URL.Path
l := len(p) l := len(p)
if p[l-1] != '/' { if p[l-1] != '/' {

View File

@ -10,7 +10,7 @@ import (
func TestStripTrailingSlash(t *testing.T) { func TestStripTrailingSlash(t *testing.T) {
req, _ := http.NewRequest(echo.GET, "/users/", nil) req, _ := http.NewRequest(echo.GET, "/users/", nil)
res := &echo.Response{Writer: httptest.NewRecorder()} res := echo.NewResponse(httptest.NewRecorder())
c := echo.NewContext(req, res, echo.New()) c := echo.NewContext(req, res, echo.New())
StripTrailingSlash()(c) StripTrailingSlash()(c)
p := c.Request.URL.Path p := c.Request.URL.Path
@ -21,7 +21,7 @@ func TestStripTrailingSlash(t *testing.T) {
func TestRedirectToSlash(t *testing.T) { func TestRedirectToSlash(t *testing.T) {
req, _ := http.NewRequest(echo.GET, "/users", nil) req, _ := http.NewRequest(echo.GET, "/users", nil)
res := &echo.Response{Writer: httptest.NewRecorder()} res := echo.NewResponse(httptest.NewRecorder())
c := echo.NewContext(req, res, echo.New()) c := echo.NewContext(req, res, echo.New())
RedirectToSlash(RedirectToSlashOptions{Code: http.StatusTemporaryRedirect})(c) RedirectToSlash(RedirectToSlashOptions{Code: http.StatusTemporaryRedirect})(c)

View File

@ -11,15 +11,23 @@ import (
type ( type (
Response struct { Response struct {
Writer http.ResponseWriter writer http.ResponseWriter
status int status int
size int64 size int64
committed bool committed bool
} }
) )
func NewResponse(w http.ResponseWriter) *Response {
return &Response{writer: w}
}
func (r *Response) Header() http.Header { func (r *Response) Header() http.Header {
return r.Writer.Header() return r.writer.Header()
}
func (r *Response) Writer() http.ResponseWriter {
return r.writer
} }
func (r *Response) WriteHeader(code int) { func (r *Response) WriteHeader(code int) {
@ -29,29 +37,29 @@ func (r *Response) WriteHeader(code int) {
return return
} }
r.status = code r.status = code
r.Writer.WriteHeader(code) r.writer.WriteHeader(code)
r.committed = true r.committed = true
} }
func (r *Response) Write(b []byte) (n int, err error) { func (r *Response) Write(b []byte) (n int, err error) {
n, err = r.Writer.Write(b) n, err = r.writer.Write(b)
r.size += int64(n) r.size += int64(n)
return n, err return n, err
} }
// Flush wraps response writer's Flush function. // Flush wraps response writer's Flush function.
func (r *Response) Flush() { func (r *Response) Flush() {
r.Writer.(http.Flusher).Flush() r.writer.(http.Flusher).Flush()
} }
// Hijack wraps response writer's Hijack function. // Hijack wraps response writer's Hijack function.
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return r.Writer.(http.Hijacker).Hijack() return r.writer.(http.Hijacker).Hijack()
} }
// CloseNotify wraps response writer's CloseNotify function. // CloseNotify wraps response writer's CloseNotify function.
func (r *Response) CloseNotify() <-chan bool { func (r *Response) CloseNotify() <-chan bool {
return r.Writer.(http.CloseNotifier).CloseNotify() return r.writer.(http.CloseNotifier).CloseNotify()
} }
func (r *Response) Status() int { func (r *Response) Status() int {
@ -63,7 +71,7 @@ func (r *Response) Size() int64 {
} }
func (r *Response) reset(w http.ResponseWriter) { func (r *Response) reset(w http.ResponseWriter) {
r.Writer = w r.writer = w
r.status = http.StatusOK r.status = http.StatusOK
r.committed = false r.committed = false
} }

View File

@ -7,7 +7,7 @@ import (
) )
func TestResponse(t *testing.T) { func TestResponse(t *testing.T) {
r := &Response{Writer: httptest.NewRecorder()} r := NewResponse(httptest.NewRecorder())
// Header // Header
if r.Header() == nil { if r.Header() == nil {

View File

@ -283,7 +283,7 @@ func TestRouterStatic(t *testing.T) {
r := New().Router r := New().Router
b := new(bytes.Buffer) b := new(bytes.Buffer)
path := "/folders/a/files/echo.gif" path := "/folders/a/files/echo.gif"
r.Add(GET, path, func(*Context) *HTTPError { r.Add(GET, path, func(*Context) error {
b.WriteString(path) b.WriteString(path)
return nil return nil
}, nil) }, nil)
@ -300,7 +300,7 @@ func TestRouterStatic(t *testing.T) {
func TestRouterParam(t *testing.T) { func TestRouterParam(t *testing.T) {
r := New().Router r := New().Router
r.Add(GET, "/users/:id", func(c *Context) *HTTPError { r.Add(GET, "/users/:id", func(c *Context) error {
return nil return nil
}, nil) }, nil)
h, _ := r.Find(GET, "/users/1", context) h, _ := r.Find(GET, "/users/1", context)
@ -315,7 +315,7 @@ func TestRouterParam(t *testing.T) {
func TestRouterTwoParam(t *testing.T) { func TestRouterTwoParam(t *testing.T) {
r := New().Router r := New().Router
r.Add(GET, "/users/:uid/files/:fid", func(*Context) *HTTPError { r.Add(GET, "/users/:uid/files/:fid", func(*Context) error {
return nil return nil
}, nil) }, nil)
@ -339,7 +339,7 @@ func TestRouterTwoParam(t *testing.T) {
func TestRouterMatchAny(t *testing.T) { func TestRouterMatchAny(t *testing.T) {
r := New().Router r := New().Router
r.Add(GET, "/users/*", func(*Context) *HTTPError { r.Add(GET, "/users/*", func(*Context) error {
return nil return nil
}, nil) }, nil)
@ -364,7 +364,7 @@ func TestRouterMatchAny(t *testing.T) {
func TestRouterMicroParam(t *testing.T) { func TestRouterMicroParam(t *testing.T) {
r := New().Router r := New().Router
r.Add(GET, "/:a/:b/:c", func(c *Context) *HTTPError { r.Add(GET, "/:a/:b/:c", func(c *Context) error {
return nil return nil
}, nil) }, nil)
h, _ := r.Find(GET, "/1/2/3", context) h, _ := r.Find(GET, "/1/2/3", context)
@ -388,11 +388,11 @@ func TestRouterMultiRoute(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
// Routes // Routes
r.Add(GET, "/users", func(*Context) *HTTPError { r.Add(GET, "/users", func(*Context) error {
b.WriteString("/users") b.WriteString("/users")
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:id", func(c *Context) *HTTPError { r.Add(GET, "/users/:id", func(c *Context) error {
return nil return nil
}, nil) }, nil)
@ -428,31 +428,31 @@ func TestRouterPriority(t *testing.T) {
r := New().Router r := New().Router
// Routes // Routes
r.Add(GET, "/users", func(c *Context) *HTTPError { r.Add(GET, "/users", func(c *Context) error {
c.Set("a", 1) c.Set("a", 1)
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/new", func(c *Context) *HTTPError { r.Add(GET, "/users/new", func(c *Context) error {
c.Set("b", 2) c.Set("b", 2)
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:id", func(c *Context) *HTTPError { r.Add(GET, "/users/:id", func(c *Context) error {
c.Set("c", 3) c.Set("c", 3)
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/dew", func(c *Context) *HTTPError { r.Add(GET, "/users/dew", func(c *Context) error {
c.Set("d", 4) c.Set("d", 4)
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:id/files", func(c *Context) *HTTPError { r.Add(GET, "/users/:id/files", func(c *Context) error {
c.Set("e", 5) c.Set("e", 5)
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/newsee", func(c *Context) *HTTPError { r.Add(GET, "/users/newsee", func(c *Context) error {
c.Set("f", 6) c.Set("f", 6)
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/*", func(c *Context) *HTTPError { r.Add(GET, "/users/*", func(c *Context) error {
c.Set("g", 7) c.Set("g", 7)
return nil return nil
}, nil) }, nil)
@ -540,14 +540,14 @@ func TestRouterParamNames(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
// Routes // Routes
r.Add(GET, "/users", func(*Context) *HTTPError { r.Add(GET, "/users", func(*Context) error {
b.WriteString("/users") b.WriteString("/users")
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:id", func(c *Context) *HTTPError { r.Add(GET, "/users/:id", func(c *Context) error {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:uid/files/:fid", func(c *Context) *HTTPError { r.Add(GET, "/users/:uid/files/:fid", func(c *Context) error {
return nil return nil
}, nil) }, nil)
@ -598,7 +598,7 @@ func TestRouterParamNames(t *testing.T) {
func TestRouterAPI(t *testing.T) { func TestRouterAPI(t *testing.T) {
r := New().Router r := New().Router
for _, route := range api { for _, route := range api {
r.Add(route.method, route.path, func(c *Context) *HTTPError { r.Add(route.method, route.path, func(c *Context) error {
for i, n := range c.pnames { for i, n := range c.pnames {
if n != "" { if n != "" {
if ":"+n != c.P(uint8(i)) { if ":"+n != c.P(uint8(i)) {
@ -619,7 +619,7 @@ func TestRouterAPI(t *testing.T) {
func TestRouterServeHTTP(t *testing.T) { func TestRouterServeHTTP(t *testing.T) {
r := New().Router r := New().Router
r.Add(GET, "/users", func(*Context) *HTTPError { r.Add(GET, "/users", func(*Context) error {
return nil return nil
}, nil) }, nil)

View File

@ -41,16 +41,16 @@ for many use cases. Restricting path parameters allows us to use memory efficien
Registers a custom `Echo.HTTPErrorHandler`. Registers a custom `Echo.HTTPErrorHandler`.
Default handler sends `HTTPError.Message` HTTP response with `HTTPError.Code` status Default handler rules
code.
- If HTTPError.Code is not set it uses `500`". - If error is of type `Echo.HTTPError` it sends HTTP response with status code `HTTPError.Code`
- If HTTPError.Message is not set it uses status code text. and message `HTTPError.Message`.
- If debug mode is enabled, HTTPError.Message is set to `HTTPError.Error.Error()`. - Else it sends `500 - Internal Server Error`.
- If debug mode is enabled, it uses `error.Error()` as status message.
### Debug ### Debug
`echo.SetDebug(on bool)` `Echo.SetDebug(on bool)`
Enables debug mode. Enables debug mode.
@ -67,12 +67,12 @@ code below registers a route for method `GET`, path `/hello` and a handler which
`Hello!` HTTP response. `Hello!` HTTP response.
```go ```go
echo.Get("/hello", func(*echo.Context) *HTTPError { echo.Get("/hello", func(*echo.Context) error {
return c.String(http.StatusOK, "Hello!") return c.String(http.StatusOK, "Hello!")
}) })
``` ```
Echo's default handler is `func(*echo.Context) *echo.HTTPError` where `echo.Context` Echo's default handler is `func(*echo.Context) error` where `echo.Context`
primarily holds HTTP request and response objects. Echo also has a support for other primarily holds HTTP request and response objects. Echo also has a support for other
types of handlers. types of handlers.
@ -89,7 +89,7 @@ or by index `echo.Context.P(i uint8) string`. Getting parameter by index gives a
slightly better performance. slightly better performance.
```go ```go
echo.Get("/users/:id", func(c *echo.Context) *HTTPError { echo.Get("/users/:id", func(c *echo.Context) error {
// By name // By name
id := c.Param("id") id := c.Param("id")
@ -119,15 +119,15 @@ match
#### Example #### Example
```go ```go
e.Get("/users/:id", func(c *echo.Context) *HTTPError { e.Get("/users/:id", func(c *echo.Context) error {
return c.String(http.StatusOK, "/users/:id") return c.String(http.StatusOK, "/users/:id")
}) })
e.Get("/users/new", func(c *echo.Context) *HTTPError { e.Get("/users/new", func(c *echo.Context) error {
return c.String(http.StatusOK, "/users/new") return c.String(http.StatusOK, "/users/new")
}) })
e.Get("/users/1/files/*", func(c *echo.Context) *HTTPError { e.Get("/users/1/files/*", func(c *echo.Context) error {
return c.String(http.StatusOK, "/users/1/files/*") return c.String(http.StatusOK, "/users/1/files/*")
}) })
``` ```
@ -152,7 +152,7 @@ application.
```go ```go
// Handler // Handler
h := func(*echo.Context) *HTTPError { h := func(*echo.Context) error {
return c.String(http.StatusOK, "OK") return c.String(http.StatusOK, "OK")
} }
@ -169,7 +169,7 @@ e.Get("/users/:id", h)
### JSON ### JSON
```go ```go
context.JSON(code int, v interface{}) *HTTPError context.JSON(code int, v interface{}) error
``` ```
Sends a JSON HTTP response with status code. Sends a JSON HTTP response with status code.
@ -177,7 +177,7 @@ Sends a JSON HTTP response with status code.
### String ### String
```go ```go
context.String(code int, s string) *HTTPError context.String(code int, s string) error
``` ```
Sends a text/plain HTTP response with status code. Sends a text/plain HTTP response with status code.
@ -185,7 +185,7 @@ Sends a text/plain HTTP response with status code.
### HTML ### HTML
```go ```go
func (c *Context) HTML(code int, html string) *HTTPError func (c *Context) HTML(code int, html string) error
``` ```
Sends an HTML HTTP response with status code. Sends an HTML HTTP response with status code.
@ -228,8 +228,8 @@ e.Favicon("public/favicon.ico")
## Error Handling ## Error Handling
Echo advocates centralized HTTP error handling by returning `*echo.HTTPError` from Echo advocates centralized HTTP error handling by returning `error` from middleware
middleware and handlers. and handlers.
It allows you to It allows you to
@ -237,8 +237,8 @@ It allows you to
- Customize HTTP responses. - Customize HTTP responses.
- Recover from panics inside middleware or handlers. - Recover from panics inside middleware or handlers.
For example, when a basic auth middleware finds invalid credentials it returns 401 For example, when a basic auth middleware finds invalid credentials it returns
"Unauthorized" error, aborting the current HTTP request. `401 - Unauthorized` error, aborting the current HTTP request.
```go ```go
package main package main
@ -251,18 +251,18 @@ import (
func main() { func main() {
e := echo.New() e := echo.New()
e.Use(func(c *echo.Context) *echo.HTTPError { e.Use(func(c *echo.Context) error {
// Extract the credentials from HTTP request header and perform a security // Extract the credentials from HTTP request header and perform a security
// check // check
// For invalid credentials // For invalid credentials
return &echo.HTTPError{Code: http.StatusUnauthorized} return echo.NewHTTPError(http.StatusUnauthorized)
}) })
e.Get("/welcome", welcome) e.Get("/welcome", welcome)
e.Run(":1323") e.Run(":1323")
} }
func welcome(c *echo.Context) *echo.HTTPError { func welcome(c *echo.Context) error {
return c.String(http.StatusOK, "Welcome!") return c.String(http.StatusOK, "Welcome!")
} }
``` ```

View File

@ -16,14 +16,14 @@ Echo is a fast HTTP router (zero memory allocation) and micro web framework in G
- `echo.MiddlewareFunc` - `echo.MiddlewareFunc`
- `func(echo.HandlerFunc) echo.HandlerFunc` - `func(echo.HandlerFunc) echo.HandlerFunc`
- `echo.HandlerFunc` - `echo.HandlerFunc`
- `func(*echo.Context) *echo.HTTPError` - `func(*echo.Context) error`
- `func(http.Handler) http.Handler` - `func(http.Handler) http.Handler`
- `http.Handler` - `http.Handler`
- `http.HandlerFunc` - `http.HandlerFunc`
- `func(http.ResponseWriter, *http.Request)` - `func(http.ResponseWriter, *http.Request)`
- Handler - Handler
- `echo.HandlerFunc` - `echo.HandlerFunc`
- `func(*echo.Context) *echo.HTTPError` - `func(*echo.Context) error`
- `http.Handler` - `http.Handler`
- `http.HandlerFunc` - `http.HandlerFunc`
- `func(http.ResponseWriter, *http.Request)` - `func(http.ResponseWriter, *http.Request)`
@ -57,7 +57,7 @@ import (
) )
// Handler // Handler
func hello(c *echo.Context) *echo.HTTPError { func hello(c *echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n") return c.String(http.StatusOK, "Hello, World!\n")
} }