1
0
mirror of https://github.com/labstack/echo.git synced 2025-09-16 09:16:29 +02:00

Default handler now returns error.

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana
2015-04-18 16:47:48 -07:00
parent 2e2e5a16a1
commit 381fbae1ff
15 changed files with 420 additions and 184 deletions

View File

@@ -2,6 +2,7 @@
Echo is a fast HTTP router (zero memory allocation) and micro web framework in Go. Echo is a fast HTTP router (zero memory allocation) and micro web framework in Go.
## Features ## Features
- Fast :rocket: router which smartly resolves conflicting routes - Fast :rocket: router which smartly resolves conflicting routes
- Extensible middleware/handler, supports: - Extensible middleware/handler, supports:
- Middleware - Middleware
@@ -21,8 +22,11 @@ Echo is a fast HTTP router (zero memory allocation) and micro web framework in G
- Serve static files, including index. - Serve static files, including index.
## Benchmark ## Benchmark
Based on [julienschmidt/go-http-routing-benchmark] (https://github.com/vishr/go-http-routing-benchmark), April 1, 2015 Based on [julienschmidt/go-http-routing-benchmark] (https://github.com/vishr/go-http-routing-benchmark), April 1, 2015
##### [GitHub API](http://developer.github.com/v3) ##### [GitHub API](http://developer.github.com/v3)
> Echo: 42728 ns/op, 0 B/op, 0 allocs/op > Echo: 42728 ns/op, 0 B/op, 0 allocs/op
``` ```
@@ -54,9 +58,11 @@ BenchmarkZeus_GithubAll 2000 752907 ns/op 300688 B/op 2648 all
``` ```
## Installation ## Installation
```go get github.com/labstack/echo``` ```go get github.com/labstack/echo```
## Example ## Example
[labstack/echo/example](https://github.com/labstack/echo/tree/master/example) [labstack/echo/example](https://github.com/labstack/echo/tree/master/example)
```go ```go
@@ -198,4 +204,5 @@ func init() {
} }
``` ```
## License ## License
[MIT](https://github.com/labstack/echo/blob/master/LICENSE) [MIT](https://github.com/labstack/echo/blob/master/LICENSE)

View File

@@ -40,13 +40,13 @@ func (c *Context) Bind(v interface{}) error {
} }
// 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. // response with status code.
func (c *Context) Render(name string, data interface{}) error { func (c *Context) Render(code int, name string, data interface{}) error {
if c.echo.renderer == nil { if c.echo.renderer == nil {
return ErrNoRenderer return ErrNoRenderer
} }
c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8") c.Response.Header().Set(HeaderContentType, MIMEHTML+"; charset=utf-8")
c.Response.WriteHeader(http.StatusOK) c.Response.WriteHeader(code)
return c.echo.renderer.Render(c.Response, name, data) return c.echo.renderer.Render(c.Response, name, data)
} }
@@ -73,6 +73,12 @@ func (c *Context) HTML(code int, html string) (err error) {
return return
} }
// NoContent sends a response with no body and a status code.
func (c *Context) NoContent(code int) error {
c.Response.WriteHeader(code)
return nil
}
// func (c *Context) File(code int, file, name string) { // func (c *Context) File(code int, file, name string) {
// } // }

View File

@@ -31,9 +31,10 @@ func TestContext(t *testing.T) {
echo: New(), echo: New(),
} }
//**********// //------
// Bind // // Bind
//**********// //------
// JSON // JSON
r.Header.Set(HeaderContentType, MIMEJSON) r.Header.Set(HeaderContentType, MIMEJSON)
u2 := new(user) u2 := new(user)
@@ -58,9 +59,10 @@ func TestContext(t *testing.T) {
} }
// TODO: add verification // TODO: add verification
//***********// //-------
// Param // // Param
//***********// //-------
// By id // By id
c.params = Params{{"id", "1"}} c.params = Params{{"id", "1"}}
if c.P(0) != "1" { if c.P(0) != "1" {
@@ -84,11 +86,11 @@ 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("hello", "Joe"); err != nil { if err := c.Render(http.StatusOK, "hello", "Joe"); err != nil {
t.Errorf("render %v", err) t.Errorf("render %v", err)
} }
c.echo.renderer = nil c.echo.renderer = nil
if err := c.Render("hello", "Joe"); err == nil { if err := c.Render(http.StatusOK, "hello", "Joe"); err == nil {
t.Error("render should error out") t.Error("render should error out")
} }

122
echo.go
View File

@@ -12,20 +12,25 @@ import (
type ( type (
Echo struct { Echo struct {
Router *router Router *router
prefix string prefix string
middleware []MiddlewareFunc middleware []MiddlewareFunc
maxParam byte maxParam byte
notFoundHandler HandlerFunc notFoundHandler HandlerFunc
binder BindFunc httpErrorHandler HTTPErrorHandler
renderer Renderer binder BindFunc
pool sync.Pool renderer Renderer
pool sync.Pool
} }
Middleware interface{} Middleware interface{}
MiddlewareFunc func(HandlerFunc) HandlerFunc MiddlewareFunc func(HandlerFunc) (HandlerFunc, error)
Handler interface{} Handler interface{}
HandlerFunc func(*Context) HandlerFunc func(*Context) error
BindFunc func(r *http.Request, v interface{}) error
// HTTPErrorHandler is a centralized HTTP error handler.
HTTPErrorHandler func(error, *Context)
BindFunc func(*http.Request, interface{}) error
// Renderer is the interface that wraps the Render method. // Renderer is the interface that wraps the Render method.
// //
@@ -81,8 +86,12 @@ var (
func New() (e *Echo) { func New() (e *Echo) {
e = &Echo{ e = &Echo{
maxParam: 5, maxParam: 5,
notFoundHandler: func(c *Context) { notFoundHandler: func(c *Context) error {
http.Error(c.Response, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(c.Response, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return nil
},
httpErrorHandler: func(err error, c *Context) {
http.Error(c.Response, err.Error(), http.StatusInternalServerError)
}, },
binder: func(r *http.Request, v interface{}) error { binder: func(r *http.Request, v interface{}) error {
ct := r.Header.Get(HeaderContentType) ct := r.Header.Get(HeaderContentType)
@@ -133,6 +142,11 @@ func (e *Echo) NotFoundHandler(h Handler) {
e.notFoundHandler = wrapH(h) e.notFoundHandler = wrapH(h)
} }
// HTTPErrorHandler registers an HTTP error handler.
func (e *Echo) HTTPErrorHandler(h HTTPErrorHandler) {
e.httpErrorHandler = h
}
// Binder registers a custom binder. It's invoked by Context.Bind API. // Binder registers a custom binder. It's invoked by Context.Bind API.
func (e *Echo) Binder(b BindFunc) { func (e *Echo) Binder(b BindFunc) {
e.binder = b e.binder = b
@@ -203,15 +217,17 @@ 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) { e.Get(path+"/*", func(c *Context) error {
fs.ServeHTTP(c.Response, c.Request) fs.ServeHTTP(c.Response, c.Request)
return nil
}) })
} }
// 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) { e.Get(path, func(c *Context) error {
http.ServeFile(c.Response, c.Request, file) http.ServeFile(c.Response, c.Request, file)
return nil
}) })
} }
@@ -230,12 +246,21 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h = e.notFoundHandler h = e.notFoundHandler
} }
c.reset(w, r, e) c.reset(w, r, e)
// Middleware // Middleware
var err error
for i := len(e.middleware) - 1; i >= 0; i-- { for i := len(e.middleware) - 1; i >= 0; i-- {
h = e.middleware[i](h) if h, err = e.middleware[i](h); err != nil {
e.httpErrorHandler(err, c)
return
}
} }
// Handler // Handler
h(c) if err := h(c); err != nil {
e.httpErrorHandler(err, c)
}
e.pool.Put(c) e.pool.Put(c)
} }
@@ -265,34 +290,50 @@ func (e *Echo) RunTLSServer(server *http.Server, certFile, keyFile string) {
func wrapM(m Middleware) MiddlewareFunc { 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, error) {
return func(c *Context) { return func(c *Context) error {
m(c) m(c)
h(c) return h(c)
} }, nil
} }
case func(HandlerFunc) HandlerFunc: case func(*Context) error:
return func(h HandlerFunc) (HandlerFunc, error) {
var err error
return func(c *Context) error {
err = m(c)
return h(c)
}, err
}
case func(HandlerFunc) (HandlerFunc, error):
return MiddlewareFunc(m) return MiddlewareFunc(m)
case func(http.Handler) http.Handler: case func(http.Handler) http.Handler:
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) (HandlerFunc, error) {
return func(c *Context) { return func(c *Context) error {
m(h).ServeHTTP(c.Response, c.Request) m(h).ServeHTTP(c.Response, c.Request)
h(c) return h(c)
} }, nil
} }
case http.Handler, http.HandlerFunc: case http.Handler, http.HandlerFunc:
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) (HandlerFunc, error) {
return func(c *Context) { return func(c *Context) error {
m.(http.Handler).ServeHTTP(c.Response, c.Request) m.(http.Handler).ServeHTTP(c.Response, c.Request)
h(c) return h(c)
} }, nil
} }
case func(http.ResponseWriter, *http.Request): case func(http.ResponseWriter, *http.Request):
return func(h HandlerFunc) HandlerFunc { return func(h HandlerFunc) (HandlerFunc, error) {
return func(c *Context) { return func(c *Context) error {
m(c.Response, c.Request) m(c.Response, c.Request)
h(c) return h(c)
} }, nil
}
case func(http.ResponseWriter, *http.Request) error:
return func(h HandlerFunc) (HandlerFunc, error) {
var err error
return func(c *Context) error {
err = m(c.Response, c.Request)
return h(c)
}, err
} }
default: default:
panic("echo: unknown middleware") panic("echo: unknown middleware")
@@ -303,14 +344,25 @@ func wrapM(m Middleware) MiddlewareFunc {
func wrapH(h Handler) HandlerFunc { func wrapH(h Handler) HandlerFunc {
switch h := h.(type) { switch h := h.(type) {
case func(*Context): case func(*Context):
return func(c *Context) error {
h(c)
return nil
}
case func(*Context) error:
return HandlerFunc(h) return HandlerFunc(h)
case http.Handler, http.HandlerFunc: case http.Handler, http.HandlerFunc:
return func(c *Context) { return func(c *Context) error {
h.(http.Handler).ServeHTTP(c.Response, c.Request) h.(http.Handler).ServeHTTP(c.Response, c.Request)
return nil
} }
case func(http.ResponseWriter, *http.Request): case func(http.ResponseWriter, *http.Request):
return func(c *Context) { return func(c *Context) error {
h(c.Response, c.Request) h(c.Response, c.Request)
return nil
}
case func(http.ResponseWriter, *http.Request) error:
return func(c *Context) error {
return h(c.Response, c.Request)
} }
default: default:
panic("echo: unknown handler") panic("echo: unknown handler")

View File

@@ -30,7 +30,7 @@ func TestEchoMaxParam(t *testing.T) {
func TestEchoIndex(t *testing.T) { func TestEchoIndex(t *testing.T) {
e := New() e := New()
e.Index("example/public/index.html") e.Index("examples/public/index.html")
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, _ := http.NewRequest(GET, "/", nil) r, _ := http.NewRequest(GET, "/", nil)
e.ServeHTTP(w, r) e.ServeHTTP(w, r)
@@ -41,7 +41,7 @@ func TestEchoIndex(t *testing.T) {
func TestEchoStatic(t *testing.T) { func TestEchoStatic(t *testing.T) {
e := New() e := New()
e.Static("/scripts", "example/public/scripts") e.Static("/scripts", "examples/public/scripts")
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, _ := http.NewRequest(GET, "/scripts/main.js", nil) r, _ := http.NewRequest(GET, "/scripts/main.js", nil)
e.ServeHTTP(w, r) e.ServeHTTP(w, r)
@@ -59,35 +59,46 @@ func TestEchoMiddleware(t *testing.T) {
b.WriteString("a") b.WriteString("a")
}) })
// func(echo.HandlerFunc) echo.HandlerFunc // func(*echo.Context) error
e.Use(func(h HandlerFunc) HandlerFunc { e.Use(func(c *Context) error {
return HandlerFunc(func(c *Context) { b.WriteString("b")
b.WriteString("b") return nil
h(c) })
})
// func(echo.HandlerFunc) (echo.HandlerFunc, error)
e.Use(func(h HandlerFunc) (HandlerFunc, error) {
return func(c *Context) error {
b.WriteString("c")
return h(c)
}, nil
}) })
// http.HandlerFunc // http.HandlerFunc
e.Use(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { e.Use(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b.WriteString("c") b.WriteString("d")
})) }))
// http.Handler // http.Handler
e.Use(http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { e.Use(http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b.WriteString("d") b.WriteString("e")
}))) })))
// func(http.Handler) http.Handler // func(http.Handler) http.Handler
e.Use(func(h http.Handler) http.Handler { e.Use(func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b.WriteString("e") b.WriteString("f")
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
}) })
}) })
// func(http.ResponseWriter, *http.Request) // func(http.ResponseWriter, *http.Request)
e.Use(func(w http.ResponseWriter, r *http.Request) { e.Use(func(w http.ResponseWriter, r *http.Request) {
b.WriteString("f") b.WriteString("g")
})
// func(http.ResponseWriter, *http.Request) error
e.Use(func(w http.ResponseWriter, r *http.Request) {
b.WriteString("h")
}) })
// Route // Route
@@ -98,8 +109,8 @@ func TestEchoMiddleware(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, _ := http.NewRequest(GET, "/hello", nil) r, _ := http.NewRequest(GET, "/hello", nil)
e.ServeHTTP(w, r) e.ServeHTTP(w, r)
if b.String() != "abcdef" { if b.String() != "abcdefgh" {
t.Errorf("buffer should be abcdef, found %s", b.String()) t.Errorf("buffer should be abcdefgh, found %s", b.String())
} }
if w.Body.String() != "world" { if w.Body.String() != "world" {
t.Error("body should be world") t.Error("body should be world")
@@ -120,10 +131,10 @@ func TestEchoHandler(t *testing.T) {
t.Error("body should be 1") t.Error("body should be 1")
} }
// http.Handler/http.HandlerFunc // func(*echo.Context) error
e.Get("/2", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { e.Get("/2", func(c *Context) {
w.Write([]byte("2")) c.String(http.StatusOK, "2")
})) })
w = httptest.NewRecorder() w = httptest.NewRecorder()
r, _ = http.NewRequest(GET, "/2", nil) r, _ = http.NewRequest(GET, "/2", nil)
e.ServeHTTP(w, r) e.ServeHTTP(w, r)
@@ -131,16 +142,39 @@ func TestEchoHandler(t *testing.T) {
t.Error("body should be 2") t.Error("body should be 2")
} }
// func(http.ResponseWriter, *http.Request) // http.Handler/http.HandlerFunc
e.Get("/3", func(w http.ResponseWriter, r *http.Request) { e.Get("/3", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("3")) w.Write([]byte("3"))
}) }))
w = httptest.NewRecorder() w = httptest.NewRecorder()
r, _ = http.NewRequest(GET, "/3", nil) r, _ = http.NewRequest(GET, "/3", nil)
e.ServeHTTP(w, r) e.ServeHTTP(w, r)
if w.Body.String() != "3" { if w.Body.String() != "3" {
t.Error("body should be 3") t.Error("body should be 3")
} }
// func(http.ResponseWriter, *http.Request)
e.Get("/4", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("4"))
})
w = httptest.NewRecorder()
r, _ = http.NewRequest(GET, "/4", nil)
e.ServeHTTP(w, r)
if w.Body.String() != "4" {
t.Error("body should be 4")
}
// func(http.ResponseWriter, *http.Request) error
e.Get("/5", func(w http.ResponseWriter, r *http.Request) error {
w.Write([]byte("5"))
return nil
})
w = httptest.NewRecorder()
r, _ = http.NewRequest(GET, "/5", nil)
e.ServeHTTP(w, r)
if w.Body.String() != "5" {
t.Error("body should be 5")
}
} }
func TestEchoGroup(t *testing.T) { func TestEchoGroup(t *testing.T) {
@@ -187,15 +221,16 @@ func TestEchoGroup(t *testing.T) {
func TestEchoMethod(t *testing.T) { func TestEchoMethod(t *testing.T) {
e := New() e := New()
e.Connect("/", func(*Context) {}) h := func(*Context) {}
e.Delete("/", func(*Context) {}) e.Connect("/", h)
e.Get("/", func(*Context) {}) e.Delete("/", h)
e.Head("/", func(*Context) {}) e.Get("/", h)
e.Options("/", func(*Context) {}) e.Head("/", h)
e.Patch("/", func(*Context) {}) e.Options("/", h)
e.Post("/", func(*Context) {}) e.Patch("/", h)
e.Put("/", func(*Context) {}) e.Post("/", h)
e.Trace("/", func(*Context) {}) e.Put("/", h)
e.Trace("/", h)
} }
func TestEchoNotFound(t *testing.T) { func TestEchoNotFound(t *testing.T) {

71
examples/crud/server.go Normal file
View File

@@ -0,0 +1,71 @@
package main
import (
"net/http"
"strconv"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
type (
user struct {
ID int
Name string
Age int
}
)
var (
users = map[int]user{}
seq = 1
)
//----------
// Handlers
//----------
func createUser(c *echo.Context) error {
u := &user{
ID: seq,
}
if err := c.Bind(u); err != nil {
return err
}
users[u.ID] = *u
seq++
return c.JSON(http.StatusCreated, u)
}
func getUser(c *echo.Context) error {
id, _ := strconv.Atoi(c.Param("id"))
return c.JSON(http.StatusOK, users[id])
}
func updateUser(c *echo.Context) error {
// id, _ := strconv.Atoi(c.Param("id"))
// users[id]
return c.NoContent(http.StatusNoContent)
}
func deleteUser(c *echo.Context) error {
id, _ := strconv.Atoi(c.Param("id"))
delete(users, id)
return c.NoContent(http.StatusNoContent)
}
func main() {
e := echo.New()
// Middleware
e.Use(mw.Logger)
// Routes
e.Post("/users", createUser)
e.Get("/users/:id", getUser)
e.Put("/users/:id", updateUser)
e.Delete("/users/:id", deleteUser)
// Start server
e.Run(":8080")
}

26
examples/hello/server.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"net/http"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
// Handler
func hello(c *echo.Context) {
c.String(http.StatusOK, "Hello, World!\n")
}
func main() {
e := echo.New()
// Middleware
e.Use(mw.Logger)
// Routes
e.Get("/", hello)
// Start server
e.Run(":8080")
}

View File

@@ -60,14 +60,14 @@ func getUser(c *echo.Context) {
func main() { func main() {
e := echo.New() e := echo.New()
//*************************// //---------------------
// Built-in middleware // // Built-in middleware
//*************************// //---------------------
e.Use(mw.Logger) e.Use(mw.Logger)
//****************************// //------------------------
// Third-party middleware // // Third-party middleware
//****************************// //------------------------
// https://github.com/rs/cors // https://github.com/rs/cors
e.Use(cors.Default().Handler) e.Use(cors.Default().Handler)
@@ -85,9 +85,9 @@ func main() {
// Serve static files // Serve static files
e.Static("/scripts", "public/scripts") e.Static("/scripts", "public/scripts")
//************// //--------
// Routes // // Routes
//************// //--------
e.Post("/users", createUser) e.Post("/users", createUser)
e.Get("/users", getUsers) e.Get("/users", getUsers)
e.Get("/users/:id", getUser) e.Get("/users/:id", getUser)

View File

@@ -1,69 +1,70 @@
package middleware package middleware
import ( //
"encoding/base64" // import (
"errors" // "encoding/base64"
"strings" // "errors"
// "strings"
"github.com/dgrijalva/jwt-go" //
"github.com/labstack/echo" // "github.com/dgrijalva/jwt-go"
) // "github.com/labstack/echo"
// )
type ( //
BasicAuthFunc func(string, string) bool // type (
AuthorizedHandler echo.HandlerFunc // BasicAuthFunc func(string, string) bool
UnauthorizedHandler func(*echo.Context, error) // AuthorizedHandler echo.HandlerFunc
JwtKeyFunc func(string) ([]byte, error) // UnauthorizedHandler func(*echo.Context, error)
Claims map[string]interface{} // JwtKeyFunc func(string) ([]byte, error)
) // Claims map[string]interface{}
// )
var ( //
ErrBasicAuth = errors.New("echo: basic auth error") // var (
ErrJwtAuth = errors.New("echo: jwt auth error") // ErrBasicAuth = errors.New("echo: basic auth error")
) // ErrJwtAuth = errors.New("echo: jwt auth error")
// )
func BasicAuth(ah AuthorizedHandler, uah UnauthorizedHandler, fn BasicAuthFunc) echo.HandlerFunc { //
return func(c *echo.Context) { // func BasicAuth(ah AuthorizedHandler, uah UnauthorizedHandler, fn BasicAuthFunc) echo.HandlerFunc {
auth := strings.Fields(c.Request.Header.Get("Authorization")) // return func(c *echo.Context) {
if len(auth) == 2 { // auth := strings.Fields(c.Request.Header.Get("Authorization"))
scheme := auth[0] // if len(auth) == 2 {
s, err := base64.StdEncoding.DecodeString(auth[1]) // scheme := auth[0]
if err != nil { // s, err := base64.StdEncoding.DecodeString(auth[1])
uah(c, err) // if err != nil {
return // uah(c, err)
} // return
cred := strings.Split(string(s), ":") // }
if scheme == "Basic" && len(cred) == 2 { // cred := strings.Split(string(s), ":")
if ok := fn(cred[0], cred[1]); ok { // if scheme == "Basic" && len(cred) == 2 {
ah(c) // if ok := fn(cred[0], cred[1]); ok {
return // ah(c)
} // return
} // }
} // }
uah(c, ErrBasicAuth) // }
} // uah(c, ErrBasicAuth)
} // }
// }
func JwtAuth(ah AuthorizedHandler, uah UnauthorizedHandler, fn JwtKeyFunc) echo.HandlerFunc { //
return func(c *echo.Context) { // func JwtAuth(ah AuthorizedHandler, uah UnauthorizedHandler, fn JwtKeyFunc) echo.HandlerFunc {
auth := strings.Fields(c.Request.Header.Get("Authorization")) // return func(c *echo.Context) {
if len(auth) == 2 { // auth := strings.Fields(c.Request.Header.Get("Authorization"))
t, err := jwt.Parse(auth[1], func(token *jwt.Token) (interface{}, error) { // if len(auth) == 2 {
if kid := token.Header["kid"]; kid != nil { // t, err := jwt.Parse(auth[1], func(token *jwt.Token) (interface{}, error) {
return fn(kid.(string)) // if kid := token.Header["kid"]; kid != nil {
} // return fn(kid.(string))
return fn("") // }
}) // return fn("")
if t.Valid { // })
c.Set("claims", Claims(t.Claims)) // if t.Valid {
ah(c) // c.Set("claims", Claims(t.Claims))
// c.Next() // ah(c)
} else { // // c.Next()
// TODO: capture errors // } else {
uah(c, err) // // TODO: capture errors
} // uah(c, err)
return // }
} // return
uah(c, ErrJwtAuth) // }
} // uah(c, ErrJwtAuth)
} // }
// }

View File

@@ -9,9 +9,9 @@ import (
"github.com/mattn/go-colorable" "github.com/mattn/go-colorable"
) )
func Logger(h echo.HandlerFunc) echo.HandlerFunc { func Logger(h echo.HandlerFunc) (echo.HandlerFunc, error) {
log.SetOutput(colorable.NewColorableStdout()) log.SetOutput(colorable.NewColorableStdout())
return echo.HandlerFunc(func(c *echo.Context) { return func(c *echo.Context) error {
start := time.Now() start := time.Now()
h(c) h(c)
end := time.Now() end := time.Now()
@@ -30,5 +30,6 @@ func Logger(h echo.HandlerFunc) echo.HandlerFunc {
} }
log.Printf("%s %s %s %s", m, p, col(s), end.Sub(start)) log.Printf("%s %s %s %s", m, p, col(s), end.Sub(start))
}) return nil
}, nil
} }

View File

@@ -7,21 +7,34 @@ import (
) )
func TestResponse(t *testing.T) { func TestResponse(t *testing.T) {
e := New() r := &response{Writer: httptest.NewRecorder()}
e.Get("/hello", func(c *Context) {
c.String(http.StatusOK, "world")
// Status // Header
if c.Response.Status() != http.StatusOK { if r.Header() == nil {
t.Error("status code should be 200") t.Error("header should not be nil")
} }
// Size // WriteHeader
if c.Response.Status() != http.StatusOK { r.WriteHeader(http.StatusOK)
t.Error("size should be 5") if r.status != http.StatusOK {
} t.Errorf("status should be %d", http.StatusOK)
}) }
w := httptest.NewRecorder() if r.committed != true {
r, _ := http.NewRequest("GET", "/hello", nil) t.Error("response should be true")
e.ServeHTTP(w, r) }
// Response already committed
r.WriteHeader(http.StatusOK)
// Status
r.status = http.StatusOK
if r.Status() != http.StatusOK {
t.Errorf("status should be %d", http.StatusOK)
}
// Write & Size
s := "echo"
r.Write([]byte(s))
if r.Size() != len(s) {
t.Errorf("size should be %d", len(s))
}
} }

View File

@@ -283,8 +283,9 @@ 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) { r.Add(GET, path, func(*Context) error {
b.WriteString(path) b.WriteString(path)
return nil
}, nil) }, nil)
h, _ := r.Find(GET, path, params) h, _ := r.Find(GET, path, params)
if h == nil { if h == nil {
@@ -298,7 +299,9 @@ 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) {}, nil) r.Add(GET, "/users/:id", func(c *Context) error {
return nil
}, nil)
h, _ := r.Find(GET, "/users/1", params) h, _ := r.Find(GET, "/users/1", params)
if h == nil { if h == nil {
t.Fatal("handler not found") t.Fatal("handler not found")
@@ -310,7 +313,9 @@ 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) {}, nil) r.Add(GET, "/users/:uid/files/:fid", func(*Context) error {
return nil
}, nil)
h, _ := r.Find(GET, "/users/1/files/1", params) h, _ := r.Find(GET, "/users/1/files/1", params)
if h == nil { if h == nil {
t.Fatal("handler not found") t.Fatal("handler not found")
@@ -325,7 +330,9 @@ func TestRouterTwoParam(t *testing.T) {
func TestRouterCatchAll(t *testing.T) { func TestRouterCatchAll(t *testing.T) {
r := New().Router r := New().Router
r.Add(GET, "/static/*", func(*Context) {}, nil) r.Add(GET, "/static/*", func(*Context) error {
return nil
}, nil)
h, _ := r.Find(GET, "/static/echo.gif", params) h, _ := r.Find(GET, "/static/echo.gif", params)
if h == nil { if h == nil {
t.Fatal("handler not found") t.Fatal("handler not found")
@@ -337,7 +344,9 @@ func TestRouterCatchAll(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) {}, nil) r.Add(GET, "/:a/:b/:c", func(c *Context) error {
return nil
}, nil)
h, _ := r.Find(GET, "/1/2/3", params) h, _ := r.Find(GET, "/1/2/3", params)
if h == nil { if h == nil {
t.Fatal("handler not found") t.Fatal("handler not found")
@@ -358,10 +367,13 @@ func TestRouterMultiRoute(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
// Routes // Routes
r.Add(GET, "/users", func(*Context) { r.Add(GET, "/users", func(*Context) error {
b.WriteString("/users") b.WriteString("/users")
return nil
}, nil)
r.Add(GET, "/users/:id", func(c *Context) error {
return nil
}, nil) }, nil)
r.Add(GET, "/users/:id", func(c *Context) {}, nil)
// Route > /users // Route > /users
h, _ := r.Find(GET, "/users", params) h, _ := r.Find(GET, "/users", params)
@@ -394,19 +406,26 @@ func TestRouterConflictingRoute(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
// Routes // Routes
r.Add(GET, "/users", func(*Context) { r.Add(GET, "/users", func(*Context) error {
b.WriteString("/users") b.WriteString("/users")
return nil
}, nil) }, nil)
r.Add(GET, "/users/new", func(*Context) { r.Add(GET, "/users/new", func(*Context) error {
b.Reset() b.Reset()
b.WriteString("/users/new") b.WriteString("/users/new")
return nil
}, nil) }, nil)
r.Add(GET, "/users/:id", func(c *Context) {}, nil) r.Add(GET, "/users/:id", func(c *Context) error {
r.Add(GET, "/users/new/moon", func(*Context) { return nil
}, nil)
r.Add(GET, "/users/new/moon", func(*Context) error {
b.Reset() b.Reset()
b.WriteString("/users/new/moon") b.WriteString("/users/new/moon")
return nil
}, nil)
r.Add(GET, "/users/new/:id", func(*Context) error {
return nil
}, nil) }, nil)
r.Add(GET, "/users/new/:id", func(*Context) {}, nil)
// Route > /users // Route > /users
h, _ := r.Find(GET, "/users", params) h, _ := r.Find(GET, "/users", params)
@@ -495,7 +514,7 @@ func TestRouterConflictingRoute(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) { r.Add(route.method, route.path, func(c *Context) error {
for _, p := range c.params { for _, p := range c.params {
if p.Name != "" { if p.Name != "" {
if ":"+p.Name != p.Value { if ":"+p.Name != p.Value {
@@ -503,6 +522,7 @@ func TestRouterAPI(t *testing.T) {
} }
} }
} }
return nil
}, nil) }, nil)
h, _ := r.Find(route.method, route.path, params) h, _ := r.Find(route.method, route.path, params)
@@ -514,7 +534,9 @@ 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) {}, nil) r.Add(GET, "/users", func(*Context) error {
return nil
}, nil)
// OK // OK
req, _ := http.NewRequest(GET, "/users", nil) req, _ := http.NewRequest(GET, "/users", nil)