mirror of
https://github.com/labstack/echo.git
synced 2025-02-03 13:11:39 +02:00
More binding and rendering functions
Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
parent
3680eb5391
commit
9d44f49ccc
@ -1,7 +1,7 @@
|
|||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- tip
|
- tip
|
||||||
before_install:
|
beforeinstall:
|
||||||
- go get github.com/modocache/gover
|
- go get github.com/modocache/gover
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
- go get golang.org/x/tools/cmd/cover
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
73
context.go
73
context.go
@ -2,19 +2,19 @@ package echo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Context represents context for the current request. It holds request and
|
// Context represents context for the current request. It holds request and
|
||||||
// response references, path parameters, data and registered handler for
|
// response references, path parameters, data and registered handler.
|
||||||
// the route.
|
|
||||||
Context struct {
|
Context struct {
|
||||||
Request *http.Request
|
Request *http.Request
|
||||||
Response *response
|
Response *response
|
||||||
params Params
|
params Params
|
||||||
store map[string]interface{}
|
store store
|
||||||
echo *Echo
|
echo *Echo
|
||||||
}
|
}
|
||||||
store map[string]interface{}
|
store map[string]interface{}
|
||||||
@ -30,40 +30,61 @@ func (c *Context) Param(n string) string {
|
|||||||
return c.params.Get(n)
|
return c.params.Get(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind decodes the payload 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{}) (err error) {
|
func (c *Context) Bind(i interface{}) error {
|
||||||
ct := c.Request.Header.Get(HeaderContentType)
|
ct := c.Request.Header.Get(HeaderContentType)
|
||||||
if strings.HasPrefix(ct, MIMEJSON) {
|
if strings.HasPrefix(ct, MIMEJSON) {
|
||||||
dec := json.NewDecoder(c.Request.Body)
|
return json.NewDecoder(c.Request.Body).Decode(i)
|
||||||
if err = dec.Decode(i); err != nil {
|
|
||||||
err = ErrBindJSON
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(ct, MIMEForm) {
|
} else if strings.HasPrefix(ct, MIMEForm) {
|
||||||
} else {
|
return nil
|
||||||
err = ErrUnsupportedContentType
|
|
||||||
}
|
}
|
||||||
return
|
return ErrUnsupportedMediaType
|
||||||
}
|
}
|
||||||
|
|
||||||
// String sends a text/plain response with status code.
|
// Render encodes the provided type and sends a response with status code
|
||||||
func (c *Context) String(n int, s string) {
|
// based on Accept header.
|
||||||
c.Response.Header().Set(HeaderContentType, MIMEText+"; charset=utf-8")
|
func (c *Context) Render(code int, i interface{}) error {
|
||||||
c.Response.WriteHeader(n)
|
a := c.Request.Header.Get(HeaderAccept)
|
||||||
c.Response.Write([]byte(s))
|
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.HTML(code, i.(string))
|
||||||
|
}
|
||||||
|
return ErrUnsupportedMediaType
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON sends an application/json response with status code.
|
// JSON sends an application/json response with status code.
|
||||||
func (c *Context) JSON(n int, i interface{}) (err error) {
|
func (c *Context) JSON(code int, i interface{}) error {
|
||||||
enc := json.NewEncoder(c.Response)
|
|
||||||
c.Response.Header().Set(HeaderContentType, MIMEJSON+"; charset=utf-8")
|
c.Response.Header().Set(HeaderContentType, MIMEJSON+"; charset=utf-8")
|
||||||
c.Response.WriteHeader(n)
|
c.Response.WriteHeader(code)
|
||||||
if err := enc.Encode(i); err != nil {
|
return json.NewEncoder(c.Response).Encode(i)
|
||||||
err = ErrRenderJSON
|
}
|
||||||
}
|
|
||||||
|
// String sends a text/plain response with status code.
|
||||||
|
func (c *Context) String(code int, s string) (err error) {
|
||||||
|
c.Response.Header().Set(HeaderContentType, MIMEText+"; charset=utf-8")
|
||||||
|
c.Response.WriteHeader(code)
|
||||||
|
_, err = c.Response.Write([]byte(s))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (c *Context) File(n int, file, name string) {
|
// HTML sends an html/plain 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTMLTemplate applies the template associated with t that has the given name to
|
||||||
|
// the specified data object and sends an html/plain response with status code.
|
||||||
|
func (c *Context) HTMLTemplate(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) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Get retrieves data from the context.
|
// Get retrieves data from the context.
|
||||||
@ -77,8 +98,8 @@ func (c *Context) Set(key string, val interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Redirect redirects the request using http.Redirect with status code.
|
// Redirect redirects the request using http.Redirect with status code.
|
||||||
func (c *Context) Redirect(n int, url string) {
|
func (c *Context) Redirect(code int, url string) {
|
||||||
http.Redirect(c.Response, c.Request, url, n)
|
http.Redirect(c.Response, c.Request, url, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) reset(rw http.ResponseWriter, r *http.Request, e *Echo) {
|
func (c *Context) reset(rw http.ResponseWriter, r *http.Request, e *Echo) {
|
||||||
|
107
context_test.go
107
context_test.go
@ -3,47 +3,82 @@ package echo
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContext(t *testing.T) {
|
func TestContext(t *testing.T) {
|
||||||
e := New()
|
b, _ := json.Marshal(u1)
|
||||||
e.Put("/users/:id", func(c *Context) {
|
r, _ := http.NewRequest(MethodPOST, "/users/1", bytes.NewReader(b))
|
||||||
u := new(user)
|
c := &Context{
|
||||||
|
Response: &response{ResponseWriter: httptest.NewRecorder()},
|
||||||
// Param
|
Request: r,
|
||||||
if c.Param("id") != "1" {
|
params: make(Params, 5),
|
||||||
t.Error("param by name, id should be 1")
|
store: make(store),
|
||||||
}
|
|
||||||
if c.P(0) != "1" {
|
|
||||||
t.Error("param by index, id should be 1")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store
|
|
||||||
c.Set("user", u.Name)
|
|
||||||
n := c.Get("user")
|
|
||||||
if n != u.Name {
|
|
||||||
t.Error("user name should be Joe")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind & JSON
|
|
||||||
if err := c.Bind(u); err == nil {
|
|
||||||
c.JSON(http.StatusCreated, u)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: fix me later
|
|
||||||
c.Redirect(http.StatusMovedPermanently, "")
|
|
||||||
})
|
|
||||||
|
|
||||||
b, _ := json.Marshal(u)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
r, _ := http.NewRequest(MethodPUT, "/users/1", bytes.NewReader(b))
|
|
||||||
r.Header.Add(HeaderContentType, MIMEJSON)
|
|
||||||
e.ServeHTTP(w, r)
|
|
||||||
if w.Code != http.StatusCreated {
|
|
||||||
t.Errorf("status code should be 201, found %d", w.Code)
|
|
||||||
}
|
}
|
||||||
verifyUser(w.Body, t)
|
|
||||||
|
//**********//
|
||||||
|
// Bind //
|
||||||
|
//**********//
|
||||||
|
r.Header.Add(HeaderContentType, MIMEJSON)
|
||||||
|
u2 := new(user)
|
||||||
|
if err := c.Bind(u2); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
verifyUser(u2, t)
|
||||||
|
|
||||||
|
//***********//
|
||||||
|
// Param //
|
||||||
|
//***********//
|
||||||
|
// By id
|
||||||
|
if c.P(0) != "" {
|
||||||
|
t.Error("param id should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// By name
|
||||||
|
if c.Param("id") != "" {
|
||||||
|
t.Error("param id should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store
|
||||||
|
c.Set("user", u1.Name)
|
||||||
|
n := c.Get("user")
|
||||||
|
if n != u1.Name {
|
||||||
|
t.Error("user name should be Joe")
|
||||||
|
}
|
||||||
|
|
||||||
|
//************//
|
||||||
|
// Render //
|
||||||
|
//************//
|
||||||
|
// JSON
|
||||||
|
r.Header.Set(HeaderAccept, MIMEJSON)
|
||||||
|
if err := c.Render(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 {
|
||||||
|
t.Errorf("render string %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML
|
||||||
|
r.Header.Set(HeaderAccept, MIMEHTML)
|
||||||
|
c.Response.committed = false
|
||||||
|
if err := c.Render(http.StatusOK, "Hello, <strong>World!</strong>"); err != nil {
|
||||||
|
t.Errorf("render html %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML template
|
||||||
|
c.Response.committed = false
|
||||||
|
tmpl, _ := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||||
|
if err := c.HTMLTemplate(http.StatusOK, tmpl, "T", "Joe"); err != nil {
|
||||||
|
t.Errorf("render html template %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect
|
||||||
|
c.Redirect(http.StatusMovedPermanently, "http://labstack.github.io/echo")
|
||||||
}
|
}
|
||||||
|
5
echo.go
5
echo.go
@ -37,6 +37,7 @@ const (
|
|||||||
|
|
||||||
MIMEJSON = "application/json"
|
MIMEJSON = "application/json"
|
||||||
MIMEText = "text/plain"
|
MIMEText = "text/plain"
|
||||||
|
MIMEHTML = "html/plain"
|
||||||
MIMEForm = "application/x-www-form-urlencoded"
|
MIMEForm = "application/x-www-form-urlencoded"
|
||||||
MIMEMultipartForm = "multipart/form-data"
|
MIMEMultipartForm = "multipart/form-data"
|
||||||
|
|
||||||
@ -60,9 +61,7 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
ErrUnsupportedContentType = errors.New("echo: unsupported content type")
|
ErrUnsupportedMediaType = errors.New("echo: unsupported media type")
|
||||||
ErrBindJSON = errors.New("echo: bind json error")
|
|
||||||
ErrRenderJSON = errors.New("echo: render json error")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// New creates a echo instance.
|
// New creates a echo instance.
|
||||||
|
20
echo_test.go
20
echo_test.go
@ -2,8 +2,6 @@ package echo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
@ -16,7 +14,7 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var u = user{
|
var u1 = user{
|
||||||
ID: "1",
|
ID: "1",
|
||||||
Name: "Joe",
|
Name: "Joe",
|
||||||
}
|
}
|
||||||
@ -230,17 +228,11 @@ func TestEchoServeHTTP(t *testing.T) {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyUser(rd io.Reader, t *testing.T) {
|
func verifyUser(u2 *user, t *testing.T) {
|
||||||
u2 := new(user)
|
if u2.ID != u1.ID {
|
||||||
dec := json.NewDecoder(rd)
|
t.Errorf("user id should be %s, found %s", u1.ID, u2.ID)
|
||||||
err := dec.Decode(u2)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
}
|
||||||
if u2.ID != u.ID {
|
if u2.Name != u1.Name {
|
||||||
t.Errorf("user id should be %s, found %s", u.ID, u2.ID)
|
t.Errorf("user name should be %s, found %s", u1.Name, u2.Name)
|
||||||
}
|
|
||||||
if u2.Name != u.Name {
|
|
||||||
t.Errorf("user name should be %s, found %s", u.Name, u2.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,9 +45,7 @@ func NewRouter(e *Echo) (r *router) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *router) Add(method, path string, h HandlerFunc, echo *Echo) {
|
func (r *router) Add(method, path string, h HandlerFunc, echo *Echo) {
|
||||||
i := 0
|
for i, l := 0, len(path); i < l; i++ {
|
||||||
l := len(path)
|
|
||||||
for ; i < l; i++ {
|
|
||||||
if path[i] == ':' {
|
if path[i] == ':' {
|
||||||
r.insert(method, path[:i], nil, pnode, echo)
|
r.insert(method, path[:i], nil, pnode, echo)
|
||||||
for ; i < l && path[i] != '/'; i++ {
|
for ; i < l && path[i] != '/'; i++ {
|
||||||
@ -179,6 +177,7 @@ func (r *router) Find(method, path string) (h HandlerFunc, c *Context, echo *Ech
|
|||||||
if l == pl {
|
if l == pl {
|
||||||
search = search[l:]
|
search = search[l:]
|
||||||
if cn.has == pnode {
|
if cn.has == pnode {
|
||||||
|
// Param node
|
||||||
cn = cn.edges[0]
|
cn = cn.edges[0]
|
||||||
i := 0
|
i := 0
|
||||||
l = len(search)
|
l = len(search)
|
||||||
@ -190,6 +189,7 @@ func (r *router) Find(method, path string) (h HandlerFunc, c *Context, echo *Ech
|
|||||||
n++
|
n++
|
||||||
search = search[i:]
|
search = search[i:]
|
||||||
} else if cn.has == anode {
|
} else if cn.has == anode {
|
||||||
|
// Catch-all node
|
||||||
p := c.params[:n+1]
|
p := c.params[:n+1]
|
||||||
p[n].Name = "_name"
|
p[n].Name = "_name"
|
||||||
p[n].Value = search
|
p[n].Value = search
|
||||||
|
@ -68,18 +68,9 @@ func TestRouterMicroParam(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterConflict(t *testing.T) {
|
|
||||||
r := New().Router
|
|
||||||
r.Add("GET", "/users/new", func(*Context) {}, nil)
|
|
||||||
r.Add("GET", "/users/wen", func(*Context) {}, nil)
|
|
||||||
r.Add("GET", "/users/:id", func(*Context) {}, nil)
|
|
||||||
r.Add("GET", "/users/*", func(*Context) {}, nil)
|
|
||||||
r.trees["GET"].printTree("", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) printTree(pfx string, tail bool) {
|
func (n *node) printTree(pfx string, tail bool) {
|
||||||
p := prefix(tail, pfx, "└── ", "├── ")
|
p := prefix(tail, pfx, "└── ", "├── ")
|
||||||
fmt.Printf("%s%s has=%d, h=%v, echo=%d\n", p, n.prefix, n.has, n.handler, n.echo)
|
fmt.Printf("%s%s has=%d, h=%v, echo=%v\n", p, n.prefix, n.has, n.handler, n.echo)
|
||||||
|
|
||||||
nodes := n.edges
|
nodes := n.edges
|
||||||
l := len(nodes)
|
l := len(nodes)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user