1
0
mirror of https://github.com/labstack/echo.git synced 2024-12-24 20:14:31 +02:00
Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2015-05-05 21:55:49 -07:00
parent 54d2f72368
commit f80fff4efb
12 changed files with 176 additions and 147 deletions

View File

@ -8,21 +8,21 @@ Echo is a fast HTTP router (zero memory allocation) and micro web framework in G
- Extensible middleware/handler, supports: - Extensible middleware/handler, supports:
- Middleware - Middleware
- `func(*echo.Context)` - `func(*echo.Context)`
- `func(*echo.Context) error` - `func(*echo.Context) *echo.HTTPError`
- `func(echo.HandlerFunc) echo.HandlerFunc` - `func(echo.HandlerFunc) echo.HandlerFunc`
- `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)`
- `func(http.ResponseWriter, *http.Request) error` - `func(http.ResponseWriter, *http.Request) *echo.HTTPError`
- Handler - Handler
- `echo.HandlerFunc` - `echo.HandlerFunc`
- `func(*echo.Context) error` - `func(*echo.Context) *echo.HTTPError`
- `func(*echo.Context)` - `func(*echo.Context)`
- `http.Handler` - `http.Handler`
- `http.HandlerFunc` - `http.HandlerFunc`
- `func(http.ResponseWriter, *http.Request)` - `func(http.ResponseWriter, *http.Request)`
- `func(http.ResponseWriter, *http.Request) error` - `func(http.ResponseWriter, *http.Request) *echo.HTTPError`
- Sub routing with groups. - Sub routing with groups.
- Handy encoding/decoding functions. - Handy encoding/decoding functions.
- Serve static files, including index. - Serve static files, including index.
@ -85,8 +85,8 @@ import (
) )
// Handler // Handler
func hello(c *echo.Context) { func hello(c *echo.Context) *echo.HTTPError {
c.String(http.StatusOK, "Hello, World!\n") return c.String(http.StatusOK, "Hello, World!\n")
} }
func main() { func main() {

View File

@ -42,15 +42,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(v interface{}) error { func (c *Context) Bind(v interface{}) *HTTPError {
return c.echo.binder(c.Request, v) return c.echo.binder(c.Request, v)
} }
// 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{}) error { func (c *Context) Render(code int, name string, data interface{}) *HTTPError {
if c.echo.renderer == nil { if c.echo.renderer == nil {
return RendererNotRegistered return &HTTPError{Error: RendererNotRegistered}
} }
c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8") c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
@ -58,37 +58,44 @@ func (c *Context) Render(code int, name string, data interface{}) error {
} }
// JSON sends an application/json response with status code. // JSON sends an application/json response with status code.
func (c *Context) JSON(code int, v interface{}) error { func (c *Context) JSON(code int, v interface{}) *HTTPError {
c.Response.Header().Set(HeaderContentType, MIMEJSON+"; charset=utf-8") c.Response.Header().Set(HeaderContentType, MIMEJSON+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
return json.NewEncoder(c.Response).Encode(v) if err := json.NewEncoder(c.Response).Encode(v); err != nil {
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) error { func (c *Context) String(code int, s string) *HTTPError {
c.Response.Header().Set(HeaderContentType, MIMEText+"; charset=utf-8") c.Response.Header().Set(HeaderContentType, MIMEText+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
_, err := c.Response.Write([]byte(s)) if _, err := c.Response.Write([]byte(s)); err != nil {
return err return &HTTPError{Error: 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) error { func (c *Context) HTML(code int, html string) *HTTPError {
c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8") c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
_, err := c.Response.Write([]byte(html)) if _, err := c.Response.Write([]byte(html)); err != nil {
return err return &HTTPError{Error: 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) error { func (c *Context) NoContent(code int) *HTTPError {
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(code int, err error) { func (c *Context) Error(he *HTTPError) {
c.echo.httpErrorHandler(code, err, c) c.echo.httpErrorHandler(he, c)
} }
// Get retrieves data from the context. // Get retrieves data from the context.

View File

@ -16,8 +16,11 @@ type (
} }
) )
func (t *Template) Render(w io.Writer, name string, data interface{}) error { func (t *Template) Render(w io.Writer, name string, data interface{}) *HTTPError {
return t.templates.ExecuteTemplate(w, name, data) if err := t.templates.ExecuteTemplate(w, name, data); err != nil {
return &HTTPError{Error: err}
}
return nil
} }
func TestContext(t *testing.T) { func TestContext(t *testing.T) {
@ -38,24 +41,24 @@ func TestContext(t *testing.T) {
// JSON // JSON
r.Header.Set(HeaderContentType, MIMEJSON) r.Header.Set(HeaderContentType, MIMEJSON)
u2 := new(user) u2 := new(user)
if err := c.Bind(u2); err != nil { if he := c.Bind(u2); he != nil {
t.Error(err) t.Errorf("bind %#v", he)
} }
verifyUser(u2, t) verifyUser(u2, t)
// FORM // FORM
r.Header.Set(HeaderContentType, MIMEForm) r.Header.Set(HeaderContentType, MIMEForm)
u2 = new(user) u2 = new(user)
if err := c.Bind(u2); err != nil { if he := c.Bind(u2); he != nil {
t.Error(err) t.Errorf("bind %#v", he)
} }
// TODO: add verification // TODO: add verification
// Unsupported // Unsupported
r.Header.Set(HeaderContentType, "") r.Header.Set(HeaderContentType, "")
u2 = new(user) u2 = new(user)
if err := c.Bind(u2); err == nil { if he := c.Bind(u2); he == nil {
t.Error(err) t.Errorf("bind %#v", he)
} }
// TODO: add verification // TODO: add verification
@ -87,33 +90,33 @@ func TestContext(t *testing.T) {
templates: template.Must(template.New("hello").Parse("{{.}}")), templates: template.Must(template.New("hello").Parse("{{.}}")),
} }
c.echo.renderer = tpl c.echo.renderer = tpl
if err := c.Render(http.StatusOK, "hello", "Joe"); err != nil { if he := c.Render(http.StatusOK, "hello", "Joe"); he != nil {
t.Errorf("render %v", err) t.Errorf("render %#v", he.Error)
} }
c.echo.renderer = nil c.echo.renderer = nil
if err := c.Render(http.StatusOK, "hello", "Joe"); err == nil { if he := c.Render(http.StatusOK, "hello", "Joe"); he.Error == nil {
t.Error("render should error out") t.Error("render should error out")
} }
// JSON // JSON
r.Header.Set(HeaderAccept, MIMEJSON) r.Header.Set(HeaderAccept, MIMEJSON)
c.Response.committed = false c.Response.committed = false
if err := c.JSON(http.StatusOK, u1); err != nil { if he := c.JSON(http.StatusOK, u1); he != nil {
t.Errorf("json %v", err) t.Errorf("json %#v", he)
} }
// String // String
r.Header.Set(HeaderAccept, MIMEText) r.Header.Set(HeaderAccept, MIMEText)
c.Response.committed = false c.Response.committed = false
if err := c.String(http.StatusOK, "Hello, World!"); err != nil { if he := c.String(http.StatusOK, "Hello, World!"); he != nil {
t.Errorf("string %v", err) t.Errorf("string %#v", he.Error)
} }
// HTML // HTML
r.Header.Set(HeaderAccept, MIMEHTML) r.Header.Set(HeaderAccept, MIMEHTML)
c.Response.committed = false c.Response.committed = false
if err := c.HTML(http.StatusOK, "Hello, <strong>World!</strong>"); err != nil { if he := c.HTML(http.StatusOK, "Hello, <strong>World!</strong>"); he != nil {
t.Errorf("html %v", err) t.Errorf("html %v", he.Error)
} }
// Redirect // Redirect

101
echo.go
View File

@ -13,7 +13,6 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/labstack/gommon/color"
"github.com/mattn/go-colorable" "github.com/mattn/go-colorable"
) )
@ -30,22 +29,27 @@ type (
uris map[Handler]string uris map[Handler]string
pool sync.Pool pool sync.Pool
} }
HTTPError struct {
Code int
Message string
Error error
}
Middleware interface{} Middleware interface{}
MiddlewareFunc func(HandlerFunc) HandlerFunc MiddlewareFunc func(HandlerFunc) HandlerFunc
Handler interface{} Handler interface{}
HandlerFunc func(*Context) error HandlerFunc func(*Context) *HTTPError
// HTTPErrorHandler is a centralized HTTP error handler. // HTTPErrorHandler is a centralized HTTP error handler.
HTTPErrorHandler func(int, error, *Context) HTTPErrorHandler func(*HTTPError, *Context)
BindFunc func(*http.Request, interface{}) error BindFunc func(*http.Request, interface{}) *HTTPError
// 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{}) error Render(w io.Writer, name string, data interface{}) *HTTPError
} }
) )
@ -125,21 +129,27 @@ func New() (e *Echo) {
e.NotFoundHandler(func(c *Context) { e.NotFoundHandler(func(c *Context) {
http.Error(c.Response, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(c.Response, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}) })
e.HTTPErrorHandler(func(code int, err error, c *Context) { e.HTTPErrorHandler(func(he *HTTPError, c *Context) {
if err != nil { if he.Code == 0 {
// TODO: Warning he.Code = http.StatusInternalServerError
log.Printf("echo: %s", color.Yellow("http error handler not registered"))
http.Error(c.Response, err.Error(), code)
} }
if he.Message == "" && he.Error != nil {
he.Message = he.Error.Error()
}
http.Error(c.Response, he.Message, he.Code)
}) })
e.Binder(func(r *http.Request, v interface{}) error { e.Binder(func(r *http.Request, v interface{}) *HTTPError {
ct := r.Header.Get(HeaderContentType) ct := r.Header.Get(HeaderContentType)
err := UnsupportedMediaType
if strings.HasPrefix(ct, MIMEJSON) { if strings.HasPrefix(ct, MIMEJSON) {
return json.NewDecoder(r.Body).Decode(v) err = json.NewDecoder(r.Body).Decode(v)
} else if strings.HasPrefix(ct, MIMEForm) { } else if strings.HasPrefix(ct, MIMEForm) {
return nil err = nil
} }
return UnsupportedMediaType if err != nil {
return &HTTPError{Error: err}
}
return nil
}) })
return return
} }
@ -272,7 +282,7 @@ func (e *Echo) add(method, path string, h Handler) {
// 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) error { e.Get(path+"/*", func(c *Context) *HTTPError {
fs.ServeHTTP(c.Response, c.Request) fs.ServeHTTP(c.Response, c.Request)
return nil return nil
}) })
@ -280,7 +290,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) error { e.Get(path, func(c *Context) *HTTPError {
http.ServeFile(c.Response, c.Request, file) http.ServeFile(c.Response, c.Request, file)
return nil return nil
}) })
@ -308,8 +318,8 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
// Execute chain // Execute chain
if err := h(c); err != nil { if he := h(c); he != nil {
e.httpErrorHandler(http.StatusInternalServerError, err, c) e.httpErrorHandler(he, c)
} }
e.pool.Put(c) e.pool.Put(c)
@ -342,16 +352,19 @@ func wrapM(m Middleware) MiddlewareFunc {
switch m := m.(type) { switch m := m.(type) {
case func(*Context): case func(*Context):
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) HandlerFunc {
return func(c *Context) error { return func(c *Context) *HTTPError {
m(c) m(c)
return h(c) if !c.Response.committed {
h(c)
}
return nil
} }
} }
case func(*Context) error: case func(*Context) *HTTPError:
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) HandlerFunc {
return func(c *Context) error { return func(c *Context) *HTTPError {
if err := m(c); err != nil { if he := m(c); he != nil {
return err return he
} }
return h(c) return h(c)
} }
@ -360,36 +373,42 @@ func wrapM(m Middleware) MiddlewareFunc {
return m return 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) (err error) { return func(c *Context) (he *HTTPError) {
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
err = h(c) he = h(c)
})).ServeHTTP(c.Response.Writer, c.Request) })).ServeHTTP(c.Response.Writer, c.Request)
return return
} }
} }
case http.Handler, http.HandlerFunc: case http.Handler, http.HandlerFunc:
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) HandlerFunc {
return func(c *Context) error { return func(c *Context) *HTTPError {
m.(http.Handler).ServeHTTP(c.Response.Writer, c.Request) m.(http.Handler).ServeHTTP(c.Response.Writer, c.Request)
return h(c) return h(c)
} }
} }
case func(http.ResponseWriter, *http.Request): case func(http.ResponseWriter, *http.Request):
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) HandlerFunc {
return func(c *Context) error { return func(c *Context) *HTTPError {
m(c.Response.Writer, c.Request) m(c.Response, c.Request)
return h(c) if !c.Response.committed {
h(c)
}
return nil
} }
} }
case func(http.ResponseWriter, *http.Request) error: case func(http.ResponseWriter, *http.Request) *HTTPError:
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) HandlerFunc {
return func(c *Context) error { return func(c *Context) *HTTPError {
if err := m(c.Response.Writer, c.Request); err != nil { if he := m(c.Response, c.Request); he != nil {
return err return he
} }
return h(c) if !c.Response.committed {
h(c)
}
return nil
} }
} }
default: default:
@ -402,25 +421,25 @@ func wrapH(h Handler) HandlerFunc {
switch h := h.(type) { switch h := h.(type) {
case HandlerFunc: case HandlerFunc:
return h return h
case func(*Context) error: case func(*Context) *HTTPError:
return h return h
case func(*Context): case func(*Context):
return func(c *Context) error { return func(c *Context) *HTTPError {
h(c) h(c)
return nil return nil
} }
case http.Handler, http.HandlerFunc: case http.Handler, http.HandlerFunc:
return func(c *Context) error { return func(c *Context) *HTTPError {
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) error { return func(c *Context) *HTTPError {
h(c.Response, c.Request) h(c.Response, c.Request)
return nil return nil
} }
case func(http.ResponseWriter, *http.Request) error: case func(http.ResponseWriter, *http.Request) *HTTPError:
return func(c *Context) error { return func(c *Context) *HTTPError {
return h(c.Response, c.Request) return h(c.Response, c.Request)
} }
default: default:

View File

@ -59,15 +59,15 @@ func TestEchoMiddleware(t *testing.T) {
b.WriteString("a") b.WriteString("a")
}) })
// func(*echo.Context) error // func(*echo.Context) *HTTPError
e.Use(func(c *Context) error { e.Use(func(c *Context) *HTTPError {
b.WriteString("b") b.WriteString("b")
return nil return nil
}) })
// func(echo.HandlerFunc) (echo.HandlerFunc, error) // func(echo.HandlerFunc) (echo.HandlerFunc, error)
e.Use(func(h HandlerFunc) HandlerFunc { e.Use(func(h HandlerFunc) HandlerFunc {
return func(c *Context) error { return func(c *Context) *HTTPError {
b.WriteString("c") b.WriteString("c")
return h(c) return h(c)
} }
@ -96,8 +96,8 @@ func TestEchoMiddleware(t *testing.T) {
b.WriteString("g") b.WriteString("g")
}) })
// func(http.ResponseWriter, *http.Request) error // func(http.ResponseWriter, *http.Request) *HTTPError
e.Use(func(w http.ResponseWriter, r *http.Request) error { e.Use(func(w http.ResponseWriter, r *http.Request) *HTTPError {
b.WriteString("h") b.WriteString("h")
return nil return nil
}) })
@ -122,7 +122,7 @@ func TestEchoHandler(t *testing.T) {
e := New() e := New()
// HandlerFunc // HandlerFunc
e.Get("/1", HandlerFunc(func(c *Context) error { e.Get("/1", HandlerFunc(func(c *Context) *HTTPError {
return c.String(http.StatusOK, "1") return c.String(http.StatusOK, "1")
})) }))
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -132,8 +132,8 @@ func TestEchoHandler(t *testing.T) {
t.Error("body should be 1") t.Error("body should be 1")
} }
// func(*echo.Context) error // func(*echo.Context) *HTTPError
e.Get("/2", func(c *Context) error { e.Get("/2", func(c *Context) *HTTPError {
return c.String(http.StatusOK, "2") return c.String(http.StatusOK, "2")
}) })
w = httptest.NewRecorder() w = httptest.NewRecorder()
@ -176,8 +176,8 @@ func TestEchoHandler(t *testing.T) {
t.Error("body should be 5") t.Error("body should be 5")
} }
// func(http.ResponseWriter, *http.Request) error // func(http.ResponseWriter, *http.Request) *HTTPError
e.Get("/6", func(w http.ResponseWriter, r *http.Request) error { e.Get("/6", func(w http.ResponseWriter, r *http.Request) *HTTPError {
w.Write([]byte("6")) w.Write([]byte("6"))
return nil return nil
}) })

View File

@ -24,34 +24,34 @@ var (
// Handlers // Handlers
//---------- //----------
func createUser(c *echo.Context) error { func createUser(c *echo.Context) *echo.HTTPError {
u := &user{ u := &user{
ID: seq, ID: seq,
} }
if err := c.Bind(u); err != nil { if he := c.Bind(u); he != nil {
return err return he
} }
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) error { func getUser(c *echo.Context) *echo.HTTPError {
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) error { func updateUser(c *echo.Context) *echo.HTTPError {
u := new(user) u := new(user)
if err := c.Bind(u); err != nil { if he := c.Bind(u); he != nil {
return err return he
} }
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) error { func deleteUser(c *echo.Context) *echo.HTTPError {
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,8 +8,8 @@ import (
) )
// Handler // Handler
func hello(c *echo.Context) { func hello(c *echo.Context) *echo.HTTPError {
c.String(http.StatusOK, "Hello, World!\n") return c.String(http.StatusOK, "Hello, World!\n")
} }
func main() { func main() {

View File

@ -29,15 +29,18 @@ var (
) )
// Render HTML // Render HTML
func (t *Template) Render(w io.Writer, name string, data interface{}) error { func (t *Template) Render(w io.Writer, name string, data interface{}) *echo.HTTPError {
return t.templates.ExecuteTemplate(w, name, data) if err := t.templates.ExecuteTemplate(w, name, data); err != nil {
return &echo.HTTPError{Error: err}
}
return nil
} }
func welcome(c *echo.Context) { func welcome(c *echo.Context) {
c.Render(http.StatusOK, "welcome", "Joe") c.Render(http.StatusOK, "welcome", "Joe")
} }
func createUser(c *echo.Context) error { func createUser(c *echo.Context) *echo.HTTPError {
u := new(user) u := new(user)
if err := c.Bind(u); err != nil { if err := c.Bind(u); err != nil {
return err return err
@ -46,11 +49,11 @@ func createUser(c *echo.Context) error {
return c.JSON(http.StatusCreated, u) return c.JSON(http.StatusCreated, u)
} }
func getUsers(c *echo.Context) error { func getUsers(c *echo.Context) *echo.HTTPError {
return c.JSON(http.StatusOK, users) return c.JSON(http.StatusOK, users)
} }
func getUser(c *echo.Context) error { func getUser(c *echo.Context) *echo.HTTPError {
return c.JSON(http.StatusOK, users[c.P(0)]) return c.JSON(http.StatusOK, users[c.P(0)])
} }

View File

@ -2,7 +2,6 @@ package middleware
import ( import (
"log" "log"
"net/http"
"time" "time"
"github.com/labstack/echo" "github.com/labstack/echo"
@ -10,10 +9,10 @@ import (
) )
func Logger(h echo.HandlerFunc) echo.HandlerFunc { func Logger(h echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error { return func(c *echo.Context) *echo.HTTPError {
start := time.Now() start := time.Now()
if err := h(c); err != nil { if he := h(c); he != nil {
c.Error(http.StatusInternalServerError, err) c.Error(he)
} }
end := time.Now() end := time.Now()
m := c.Request.Method m := c.Request.Method

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) error { r.Add(GET, path, func(*Context) *HTTPError {
b.WriteString(path) b.WriteString(path)
return nil return nil
}, nil) }, nil)
@ -299,7 +299,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) error { r.Add(GET, "/users/:id", func(c *Context) *HTTPError {
return nil return nil
}, nil) }, nil)
h, _ := r.Find(GET, "/users/1", context) h, _ := r.Find(GET, "/users/1", context)
@ -313,7 +313,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) error { r.Add(GET, "/users/:uid/files/:fid", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
@ -336,7 +336,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) error { r.Add(GET, "/users/*", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
@ -359,7 +359,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) error { r.Add(GET, "/:a/:b/:c", func(c *Context) *HTTPError {
return nil return nil
}, nil) }, nil)
h, _ := r.Find(GET, "/1/2/3", context) h, _ := r.Find(GET, "/1/2/3", context)
@ -382,11 +382,11 @@ func TestRouterMultiRoute(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
// Routes // Routes
r.Add(GET, "/users", func(*Context) error { r.Add(GET, "/users", func(*Context) *HTTPError {
b.WriteString("/users") b.WriteString("/users")
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:id", func(c *Context) error { r.Add(GET, "/users/:id", func(c *Context) *HTTPError {
return nil return nil
}, nil) }, nil)
@ -421,24 +421,24 @@ func TestRouterConflictingRoute(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
// Routes // Routes
r.Add(GET, "/users", func(*Context) error { r.Add(GET, "/users", func(*Context) *HTTPError {
b.WriteString("/users") b.WriteString("/users")
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/new", func(*Context) error { r.Add(GET, "/users/new", func(*Context) *HTTPError {
b.Reset() b.Reset()
b.WriteString("/users/new") b.WriteString("/users/new")
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:id", func(c *Context) error { r.Add(GET, "/users/:id", func(c *Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/new/moon", func(*Context) error { r.Add(GET, "/users/new/moon", func(*Context) *HTTPError {
b.Reset() b.Reset()
b.WriteString("/users/new/moon") b.WriteString("/users/new/moon")
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/new/:id", func(*Context) error { r.Add(GET, "/users/new/:id", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
@ -532,14 +532,14 @@ func TestRouterParamNames(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
// Routes // Routes
r.Add(GET, "/users", func(*Context) error { r.Add(GET, "/users", func(*Context) *HTTPError {
b.WriteString("/users") b.WriteString("/users")
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:id", func(c *Context) error { r.Add(GET, "/users/:id", func(c *Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:uid/files/:fid", func(c *Context) error { r.Add(GET, "/users/:uid/files/:fid", func(c *Context) *HTTPError {
return nil return nil
}, nil) }, nil)
@ -587,7 +587,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) error { r.Add(route.method, route.path, func(c *Context) *HTTPError {
for i, n := range c.pnames { for i, n := range c.pnames {
if n != "" { if n != "" {
if ":"+n != c.pvalues[i] { if ":"+n != c.pvalues[i] {
@ -607,7 +607,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) error { r.Add(GET, "/users", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
@ -624,31 +624,31 @@ func TestRouterServeHTTP(t *testing.T) {
func TestRouterExperiment(t *testing.T) { func TestRouterExperiment(t *testing.T) {
r := New().Router r := New().Router
r.Add(GET, "/use", func(*Context) error { r.Add(GET, "/use", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/*", func(*Context) error { r.Add(GET, "/users/*", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/", func(*Context) error { r.Add(GET, "/users/", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/new/*", func(*Context) error { r.Add(GET, "/users/new/*", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/new", func(*Context) error { r.Add(GET, "/users/new", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:uid", func(*Context) error { r.Add(GET, "/users/:uid", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/new/:id", func(*Context) error { r.Add(GET, "/users/new/:id", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/wen", func(*Context) error { r.Add(GET, "/users/wen", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)
r.Add(GET, "/users/:uid/files/:fid", func(*Context) error { r.Add(GET, "/users/:uid/files/:fid", func(*Context) *HTTPError {
return nil return nil
}, nil) }, nil)

View File

@ -48,9 +48,7 @@ Default handler sends 404 "Not Found" response.
`echo.HTTPErrorHandler(h HTTPErrorHandler)` `echo.HTTPErrorHandler(h HTTPErrorHandler)`
Registers a centralized HTTP error handler. Registers a custom centralized HTTP error handler.
Default http error handler sends 500 "Internal Server Error" response.
## Routing ## Routing
@ -70,9 +68,9 @@ echo.Get("/hello", func(*echo.Context) {
}) })
``` ```
Echo's default handler is `func(*echo.Context) error` where `echo.Context` primarily Echo's default handler is `func(*echo.Context) *echo.HTTPError` where `echo.Context`
holds request and response objects. Echo also has a support for other types of primarily holds request and response objects. Echo also has a support for other
handlers. types of handlers.
<!-- TODO mention about not able to take advantage --> <!-- TODO mention about not able to take advantage -->

View File

@ -14,21 +14,21 @@ Echo is a fast HTTP router (zero memory allocation) and micro web framework in G
- Extensible middleware/handler, supports: - Extensible middleware/handler, supports:
- Middleware - Middleware
- `func(*echo.Context)` - `func(*echo.Context)`
- `func(*echo.Context) error` - `func(*echo.Context) *echo.HTTPError`
- `func(echo.HandlerFunc) echo.HandlerFunc` - `func(echo.HandlerFunc) echo.HandlerFunc`
- `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)`
- `func(http.ResponseWriter, *http.Request) error` - `func(http.ResponseWriter, *http.Request) *echo.HTTPError`
- Handler - Handler
- `echo.HandlerFunc` - `echo.HandlerFunc`
- `func(*echo.Context) error` - `func(*echo.Context) *echo.HTTPError`
- `func(*echo.Context)` - `func(*echo.Context)`
- `http.Handler` - `http.Handler`
- `http.HandlerFunc` - `http.HandlerFunc`
- `func(http.ResponseWriter, *http.Request)` - `func(http.ResponseWriter, *http.Request)`
- `func(http.ResponseWriter, *http.Request) error` - `func(http.ResponseWriter, *http.Request) *echo.HTTPError`
- Sub routing with groups. - Sub routing with groups.
- Handy encoding/decoding functions. - Handy encoding/decoding functions.
- Serve static files, including index. - Serve static files, including index.
@ -59,8 +59,8 @@ import (
) )
// Handler // Handler
func hello(c *echo.Context) { func hello(c *echo.Context) *echo.HTTPError {
c.String(http.StatusOK, "Hello, World!\n") return c.String(http.StatusOK, "Hello, World!\n")
} }
func main() { func main() {
@ -80,8 +80,8 @@ func main() {
`echo.New()` returns a new instance of Echo. `echo.New()` returns a new instance of Echo.
`e.Use(mw.Logger)` adds logging middleware to the chain. It logs every request made to the server, `e.Use(mw.Logger)` adds logging middleware to the chain. It logs every request
producing output made to the server, producing output
```sh ```sh
2015/04/25 12:15:20 GET / 200 7.544µs 2015/04/25 12:15:20 GET / 200 7.544µs