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:
@@ -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)
|
||||||
|
12
context.go
12
context.go
@@ -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) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@@ -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
122
echo.go
@@ -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")
|
||||||
|
95
echo_test.go
95
echo_test.go
@@ -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
71
examples/crud/server.go
Normal 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
26
examples/hello/server.go
Normal 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")
|
||||||
|
}
|
@@ -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)
|
@@ -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)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
Reference in New Issue
Block a user