1
0
mirror of https://github.com/labstack/echo.git synced 2025-01-26 03:20:08 +02:00
Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2015-04-07 13:02:23 -07:00
parent cff9ce5ae6
commit a95d6668f3
7 changed files with 51 additions and 77 deletions

View File

@ -16,7 +16,7 @@ Echo is a fast HTTP router (zero memory allocation) + micro web framework in Go.
- `http.Handler` - `http.Handler`
- `http.HandlerFunc` - `http.HandlerFunc`
- `func(http.ResponseWriter, *http.Request)` - `func(http.ResponseWriter, *http.Request)`
- Sub/Group routing - Group routing
- Handy encoding/decoding functions. - Handy encoding/decoding functions.
- Serve static files, including index. - Serve static files, including index.

View File

@ -2,7 +2,6 @@ package echo
import ( import (
"encoding/json" "encoding/json"
"html/template"
"net/http" "net/http"
"strings" "strings"
) )
@ -36,34 +35,27 @@ func (c *Context) Param(name string) (value string) {
} }
// Bind decodes the body into provided type based on Content-Type header. // Bind decodes the body into provided type based on Content-Type header.
func (c *Context) Bind(i interface{}) error { func (c *Context) Bind(v interface{}) error {
ct := c.Request.Header.Get(HeaderContentType) ct := c.Request.Header.Get(HeaderContentType)
if strings.HasPrefix(ct, MIMEJSON) { if strings.HasPrefix(ct, MIMEJSON) {
return json.NewDecoder(c.Request.Body).Decode(i) return json.NewDecoder(c.Request.Body).Decode(v)
} else if strings.HasPrefix(ct, MIMEForm) { } else if strings.HasPrefix(ct, MIMEForm) {
return nil return nil
} }
return ErrUnsupportedMediaType return ErrUnsupportedMediaType
} }
// Render encodes the provided type and sends a response with status code func (c *Context) Render(code int, name string, data interface{}) error {
// based on Accept header. If Accept header not set, it defaults to html/plain. c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8")
func (c *Context) Render(code int, i interface{}) error { c.Response.WriteHeader(code)
a := c.Request.Header.Get(HeaderAccept) return c.echo.renderFunc(c.Response.ResponseWriter, name, data)
if strings.HasPrefix(a, MIMEJSON) {
return c.JSON(code, i)
} else if strings.HasPrefix(a, MIMEText) {
return c.String(code, i.(string))
} else if strings.HasPrefix(a, MIMEHTML) {
}
return c.HTMLString(code, i.(string))
} }
// 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{}) error { func (c *Context) JSON(code int, v interface{}) error {
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(i) return json.NewEncoder(c.Response).Encode(v)
} }
// String sends a text/plain response with status code. // String sends a text/plain response with status code.
@ -74,20 +66,14 @@ func (c *Context) String(code int, s string) (err error) {
return return
} }
// HTMLString sends a text/html response with status code. // HTML sends a text/html response with status code.
func (c *Context) HTMLString(code int, html string) (err error) { func (c *Context) HTML(code int, html string) (err error) {
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)) _, err = c.Response.Write([]byte(html))
return return
} }
// HTML applies the template associated with t that has the given name to
// the specified data object and sends a text/html response with status code.
func (c *Context) HTML(code int, t *template.Template, name string, data interface{}) (err error) {
return t.ExecuteTemplate(c.Response, name, data)
}
// func (c *Context) File(code int, file, name string) { // func (c *Context) File(code int, file, name string) {
// } // }
@ -106,8 +92,8 @@ func (c *Context) Redirect(code int, url string) {
http.Redirect(c.Response, c.Request, url, code) http.Redirect(c.Response, c.Request, url, code)
} }
func (c *Context) reset(rw http.ResponseWriter, r *http.Request, e *Echo) { func (c *Context) reset(w http.ResponseWriter, r *http.Request, e *Echo) {
c.Response.reset(rw) c.Response.reset(w)
c.Request = r c.Request = r
c.echo = e c.echo = e
} }

View File

@ -3,7 +3,6 @@ package echo
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"html/template"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -67,36 +66,27 @@ func TestContext(t *testing.T) {
t.Error("user name should be Joe") t.Error("user name should be Joe")
} }
//************//
// Render //
//************//
// JSON // JSON
r.Header.Set(HeaderAccept, MIMEJSON) r.Header.Set(HeaderAccept, MIMEJSON)
if err := c.Render(http.StatusOK, u1); err != nil { if err := c.JSON(http.StatusOK, u1); err != nil {
t.Errorf("render json %v", err) t.Errorf("render json %v", err)
} }
// String // String
r.Header.Set(HeaderAccept, MIMEText) r.Header.Set(HeaderAccept, MIMEText)
c.Response.committed = false c.Response.committed = false
if err := c.Render(http.StatusOK, "Hello, World!"); err != nil { if err := c.String(http.StatusOK, "Hello, World!"); err != nil {
t.Errorf("render string %v", err) t.Errorf("render string %v", err)
} }
// HTML string // HTML
r.Header.Set(HeaderAccept, MIMEHTML) r.Header.Set(HeaderAccept, MIMEHTML)
c.Response.committed = false c.Response.committed = false
if err := c.Render(http.StatusOK, "Hello, <strong>World!</strong>"); err != nil { if err := c.HTML(http.StatusOK, "Hello, <strong>World!</strong>"); err != nil {
t.Errorf("render html %v", err) t.Errorf("render html %v", err)
} }
// HTML
c.Response.committed = false
tmpl, _ := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
if err := c.HTML(http.StatusOK, tmpl, "T", "Joe"); err != nil {
t.Errorf("render html template %v", err)
}
// Redirect // Redirect
c.Response.committed = false
c.Redirect(http.StatusMovedPermanently, "http://labstack.github.io/echo") c.Redirect(http.StatusMovedPermanently, "http://labstack.github.io/echo")
} }

29
echo.go
View File

@ -2,6 +2,7 @@ package echo
import ( import (
"errors" "errors"
"io"
"log" "log"
"net/http" "net/http"
"sync" "sync"
@ -14,12 +15,14 @@ type (
middleware []MiddlewareFunc middleware []MiddlewareFunc
maxParam byte maxParam byte
notFoundHandler HandlerFunc notFoundHandler HandlerFunc
renderFunc RenderFunc
pool sync.Pool pool sync.Pool
} }
Middleware interface{} Middleware interface{}
MiddlewareFunc func(HandlerFunc) HandlerFunc MiddlewareFunc func(HandlerFunc) HandlerFunc
Handler interface{} Handler interface{}
HandlerFunc func(*Context) HandlerFunc func(*Context)
RenderFunc func(io.Writer, string, interface{}) error
) )
const ( const (
@ -69,6 +72,9 @@ func New() (e *Echo) {
notFoundHandler: func(c *Context) { notFoundHandler: func(c *Context) {
http.Error(c.Response, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(c.Response, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}, },
renderFunc: func(w io.Writer, name string, data interface{}) (err error) {
return
},
} }
e.Router = NewRouter(e) e.Router = NewRouter(e)
e.pool.New = func() interface{} { e.pool.New = func() interface{} {
@ -86,20 +92,15 @@ func New() (e *Echo) {
func (h HandlerFunc) ServeHTTP(http.ResponseWriter, *http.Request) { func (h HandlerFunc) ServeHTTP(http.ResponseWriter, *http.Request) {
} }
// Sub creates a new sub router. It inherits all properties from the parent // Group creates a sub router. It inherits all properties from the parent.
// router, including middleware. // Passing middleware overrides parent middleware.
func (e *Echo) Sub(pfx string) *Echo { func (e *Echo) Group(pfx string, m ...Middleware) *Echo {
s := *e
s.prefix = pfx
return &s
}
// Group is similar to Sub but excludes inheriting middleware from the parent
// router.
func (e *Echo) Group(pfx string) *Echo {
g := *e g := *e
g.prefix = pfx g.prefix = pfx
g.middleware = nil if len(m) > 0 {
g.middleware = nil
g.Use(m...)
}
return &g return &g
} }
@ -190,7 +191,7 @@ func (e *Echo) Index(file string) {
e.ServeFile("/", file) e.ServeFile("/", file)
} }
func (e *Echo) ServeHTTP(rw http.ResponseWriter, r *http.Request) { func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h, c, echo := e.Router.Find(r.Method, r.URL.Path) h, c, echo := e.Router.Find(r.Method, r.URL.Path)
defer e.pool.Put(c) defer e.pool.Put(c)
if echo != nil { if echo != nil {
@ -199,7 +200,7 @@ func (e *Echo) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if h == nil { if h == nil {
h = e.notFoundHandler h = e.notFoundHandler
} }
c.reset(rw, r, e) c.reset(w, r, e)
// Middleware // Middleware
for i := len(e.middleware) - 1; i >= 0; i-- { for i := len(e.middleware) - 1; i >= 0; i-- {
h = e.middleware[i](h) h = e.middleware[i](h)

View File

@ -143,27 +143,13 @@ func TestEchoHandler(t *testing.T) {
} }
} }
func TestEchoSubGroup(t *testing.T) { func TestEchoGroup(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
e := New() e := New()
e.Use(func(*Context) { e.Use(func(*Context) {
b.WriteString("1") b.WriteString("1")
}) })
e.Get("/users", func(*Context) {}) e.Get("/users", func(*Context) {})
s := e.Sub("/sub")
s.Use(func(*Context) {
b.WriteString("2")
})
s.Get("/home", func(*Context) {})
g := e.Group("/group")
g.Use(func(*Context) {
b.WriteString("3")
})
g.Get("/home", func(*Context) {})
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)
@ -171,17 +157,28 @@ func TestEchoSubGroup(t *testing.T) {
t.Errorf("should only execute middleware 1, executed %s", b.String()) t.Errorf("should only execute middleware 1, executed %s", b.String())
} }
// Group
g1 := e.Group("/group1")
g1.Use(func(*Context) {
b.WriteString("2")
})
g1.Get("/home", func(*Context) {})
b.Reset() b.Reset()
w = httptest.NewRecorder() w = httptest.NewRecorder()
r, _ = http.NewRequest(GET, "/sub/home", nil) r, _ = http.NewRequest(GET, "/group1/home", nil)
e.ServeHTTP(w, r) e.ServeHTTP(w, r)
if b.String() != "12" { if b.String() != "12" {
t.Errorf("should execute middleware 1 & 2, executed %s", b.String()) t.Errorf("should execute middleware 1 & 2, executed %s", b.String())
} }
// Group with no parent middleware
g2 := e.Group("/group2", func(*Context) {
b.WriteString("3")
})
g2.Get("/home", func(*Context) {})
b.Reset() b.Reset()
w = httptest.NewRecorder() w = httptest.NewRecorder()
r, _ = http.NewRequest(GET, "/group/home", nil) r, _ = http.NewRequest(GET, "/group2/home", nil)
e.ServeHTTP(w, r) e.ServeHTTP(w, r)
if b.String() != "3" { if b.String() != "3" {
t.Errorf("should execute middleware 3, executed %s", b.String()) t.Errorf("should execute middleware 3, executed %s", b.String())

View File

@ -65,7 +65,7 @@ func (r *response) Size() int {
return r.size return r.size
} }
func (r *response) reset(rw http.ResponseWriter) { func (r *response) reset(w http.ResponseWriter) {
r.ResponseWriter = rw r.ResponseWriter = w
r.committed = false r.committed = false
} }

View File

@ -214,10 +214,10 @@ func (r *router) Find(method, path string) (h HandlerFunc, c *Context, echo *Ech
} }
} }
func (r *router) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h, c, _ := r.Find(req.Method, req.URL.Path) h, c, _ := r.Find(req.Method, req.URL.Path)
defer r.echo.pool.Put(c) defer r.echo.pool.Put(c)
c.Response.ResponseWriter = rw c.Response.ResponseWriter = w
if h != nil { if h != nil {
h(c) h(c)
} else { } else {