mirror of
https://github.com/labstack/echo.git
synced 2025-01-26 03:20:08 +02:00
parent
cff9ce5ae6
commit
a95d6668f3
@ -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.
|
||||||
|
|
||||||
|
38
context.go
38
context.go
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
29
echo.go
@ -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)
|
||||||
|
31
echo_test.go
31
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)
|
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())
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user