From a95d6668f3b029f93ea4e932a0a61caf1bd1eb79 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Tue, 7 Apr 2015 13:02:23 -0700 Subject: [PATCH] #10 Signed-off-by: Vishal Rana --- README.md | 2 +- context.go | 38 ++++++++++++-------------------------- context_test.go | 20 +++++--------------- echo.go | 29 +++++++++++++++-------------- echo_test.go | 31 ++++++++++++++----------------- response.go | 4 ++-- router.go | 4 ++-- 7 files changed, 51 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index c48413a1..47a556c5 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Echo is a fast HTTP router (zero memory allocation) + micro web framework in Go. - `http.Handler` - `http.HandlerFunc` - `func(http.ResponseWriter, *http.Request)` -- Sub/Group routing +- Group routing - Handy encoding/decoding functions. - Serve static files, including index. diff --git a/context.go b/context.go index 079d3ac1..f559e71f 100644 --- a/context.go +++ b/context.go @@ -2,7 +2,6 @@ package echo import ( "encoding/json" - "html/template" "net/http" "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. -func (c *Context) Bind(i interface{}) error { +func (c *Context) Bind(v interface{}) error { ct := c.Request.Header.Get(HeaderContentType) 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) { return nil } return ErrUnsupportedMediaType } -// Render encodes the provided type and sends a response with status code -// based on Accept header. If Accept header not set, it defaults to html/plain. -func (c *Context) Render(code int, i interface{}) error { - a := c.Request.Header.Get(HeaderAccept) - 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)) +func (c *Context) Render(code int, name string, data interface{}) error { + c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8") + c.Response.WriteHeader(code) + return c.echo.renderFunc(c.Response.ResponseWriter, name, data) } // 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.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. @@ -74,20 +66,14 @@ func (c *Context) String(code int, s string) (err error) { return } -// HTMLString sends a text/html response with status code. -func (c *Context) HTMLString(code int, html string) (err error) { +// HTML sends a text/html response with status code. +func (c *Context) HTML(code int, html string) (err error) { c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8") c.Response.WriteHeader(code) _, err = c.Response.Write([]byte(html)) 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) { // } @@ -106,8 +92,8 @@ func (c *Context) Redirect(code int, url string) { http.Redirect(c.Response, c.Request, url, code) } -func (c *Context) reset(rw http.ResponseWriter, r *http.Request, e *Echo) { - c.Response.reset(rw) +func (c *Context) reset(w http.ResponseWriter, r *http.Request, e *Echo) { + c.Response.reset(w) c.Request = r c.echo = e } diff --git a/context_test.go b/context_test.go index 78eb8573..4b32a58b 100644 --- a/context_test.go +++ b/context_test.go @@ -3,7 +3,6 @@ package echo import ( "bytes" "encoding/json" - "html/template" "net/http" "net/http/httptest" "testing" @@ -67,36 +66,27 @@ func TestContext(t *testing.T) { t.Error("user name should be Joe") } - //************// - // Render // - //************// // JSON 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) } // String r.Header.Set(HeaderAccept, MIMEText) 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) } - // HTML string + // HTML r.Header.Set(HeaderAccept, MIMEHTML) c.Response.committed = false - if err := c.Render(http.StatusOK, "Hello, World!"); err != nil { + if err := c.HTML(http.StatusOK, "Hello, World!"); err != nil { 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 + c.Response.committed = false c.Redirect(http.StatusMovedPermanently, "http://labstack.github.io/echo") } diff --git a/echo.go b/echo.go index 48cd8dc8..04d254d0 100644 --- a/echo.go +++ b/echo.go @@ -2,6 +2,7 @@ package echo import ( "errors" + "io" "log" "net/http" "sync" @@ -14,12 +15,14 @@ type ( middleware []MiddlewareFunc maxParam byte notFoundHandler HandlerFunc + renderFunc RenderFunc pool sync.Pool } Middleware interface{} MiddlewareFunc func(HandlerFunc) HandlerFunc Handler interface{} HandlerFunc func(*Context) + RenderFunc func(io.Writer, string, interface{}) error ) const ( @@ -69,6 +72,9 @@ func New() (e *Echo) { notFoundHandler: func(c *Context) { 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.pool.New = func() interface{} { @@ -86,20 +92,15 @@ func New() (e *Echo) { func (h HandlerFunc) ServeHTTP(http.ResponseWriter, *http.Request) { } -// Sub creates a new sub router. It inherits all properties from the parent -// router, including middleware. -func (e *Echo) Sub(pfx string) *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 { +// Group creates a sub router. It inherits all properties from the parent. +// Passing middleware overrides parent middleware. +func (e *Echo) Group(pfx string, m ...Middleware) *Echo { g := *e g.prefix = pfx - g.middleware = nil + if len(m) > 0 { + g.middleware = nil + g.Use(m...) + } return &g } @@ -190,7 +191,7 @@ func (e *Echo) Index(file string) { 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) defer e.pool.Put(c) if echo != nil { @@ -199,7 +200,7 @@ func (e *Echo) ServeHTTP(rw http.ResponseWriter, r *http.Request) { if h == nil { h = e.notFoundHandler } - c.reset(rw, r, e) + c.reset(w, r, e) // Middleware for i := len(e.middleware) - 1; i >= 0; i-- { h = e.middleware[i](h) diff --git a/echo_test.go b/echo_test.go index 8ac30cae..d31d5e07 100644 --- a/echo_test.go +++ b/echo_test.go @@ -143,27 +143,13 @@ func TestEchoHandler(t *testing.T) { } } -func TestEchoSubGroup(t *testing.T) { +func TestEchoGroup(t *testing.T) { b := new(bytes.Buffer) - e := New() e.Use(func(*Context) { b.WriteString("1") }) 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() r, _ := http.NewRequest(GET, "/users", nil) e.ServeHTTP(w, r) @@ -171,17 +157,28 @@ func TestEchoSubGroup(t *testing.T) { 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() w = httptest.NewRecorder() - r, _ = http.NewRequest(GET, "/sub/home", nil) + r, _ = http.NewRequest(GET, "/group1/home", nil) e.ServeHTTP(w, r) if b.String() != "12" { 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() w = httptest.NewRecorder() - r, _ = http.NewRequest(GET, "/group/home", nil) + r, _ = http.NewRequest(GET, "/group2/home", nil) e.ServeHTTP(w, r) if b.String() != "3" { t.Errorf("should execute middleware 3, executed %s", b.String()) diff --git a/response.go b/response.go index ab075ce7..38564613 100644 --- a/response.go +++ b/response.go @@ -65,7 +65,7 @@ func (r *response) Size() int { return r.size } -func (r *response) reset(rw http.ResponseWriter) { - r.ResponseWriter = rw +func (r *response) reset(w http.ResponseWriter) { + r.ResponseWriter = w r.committed = false } diff --git a/router.go b/router.go index 15cc2858..6a635960 100644 --- a/router.go +++ b/router.go @@ -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) defer r.echo.pool.Put(c) - c.Response.ResponseWriter = rw + c.Response.ResponseWriter = w if h != nil { h(c) } else {