mirror of
https://github.com/labstack/echo.git
synced 2024-12-24 20:14:31 +02:00
- Moved static file serving to a new handler package
- Middleware at route level - Group middleware is a in closure now Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
parent
6bb871fe3a
commit
51acf465fe
50
context.go
50
context.go
@ -3,7 +3,10 @@ package echo
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -42,10 +45,11 @@ type (
|
|||||||
JSONP(int, string, interface{}) error
|
JSONP(int, string, interface{}) error
|
||||||
XML(int, interface{}) error
|
XML(int, interface{}) error
|
||||||
XMLBlob(int, []byte) error
|
XMLBlob(int, []byte) error
|
||||||
File(string, string, bool) error
|
Attachment(string) error
|
||||||
NoContent(int) error
|
NoContent(int) error
|
||||||
Redirect(int, string) error
|
Redirect(int, string) error
|
||||||
Error(err error)
|
Error(err error)
|
||||||
|
Handle(Context) error
|
||||||
Logger() logger.Logger
|
Logger() logger.Logger
|
||||||
Object() *context
|
Object() *context
|
||||||
}
|
}
|
||||||
@ -59,12 +63,17 @@ type (
|
|||||||
pvalues []string
|
pvalues []string
|
||||||
query url.Values
|
query url.Values
|
||||||
store store
|
store store
|
||||||
|
handler Handler
|
||||||
echo *Echo
|
echo *Echo
|
||||||
}
|
}
|
||||||
|
|
||||||
store map[string]interface{}
|
store map[string]interface{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
indexPage = "index.html"
|
||||||
|
)
|
||||||
|
|
||||||
// NewContext creates a Context object.
|
// NewContext creates a Context object.
|
||||||
func NewContext(req engine.Request, res engine.Response, e *Echo) Context {
|
func NewContext(req engine.Request, res engine.Response, e *Echo) Context {
|
||||||
return &context{
|
return &context{
|
||||||
@ -73,9 +82,14 @@ func NewContext(req engine.Request, res engine.Response, e *Echo) Context {
|
|||||||
echo: e,
|
echo: e,
|
||||||
pvalues: make([]string, *e.maxParam),
|
pvalues: make([]string, *e.maxParam),
|
||||||
store: make(store),
|
store: make(store),
|
||||||
|
handler: notFoundHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *context) Handle(ctx Context) error {
|
||||||
|
return c.handler.Handle(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *context) Deadline() (deadline time.Time, ok bool) {
|
func (c *context) Deadline() (deadline time.Time, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -166,7 +180,7 @@ func (c *context) Bind(i interface{}) error {
|
|||||||
// code. Templates can be registered using `Echo.SetRenderer()`.
|
// code. Templates can be registered using `Echo.SetRenderer()`.
|
||||||
func (c *context) Render(code int, name string, data interface{}) (err error) {
|
func (c *context) Render(code int, name string, data interface{}) (err error) {
|
||||||
if c.echo.renderer == nil {
|
if c.echo.renderer == nil {
|
||||||
return RendererNotRegistered
|
return ErrRendererNotRegistered
|
||||||
}
|
}
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
if err = c.echo.renderer.Render(buf, name, data); err != nil {
|
if err = c.echo.renderer.Render(buf, name, data); err != nil {
|
||||||
@ -250,17 +264,17 @@ func (c *context) XMLBlob(code int, b []byte) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// File sends a response with the content of the file. If `attachment` is set
|
// Attachment sends specified file as an attachment to the client.
|
||||||
// to true, the client is prompted to save the file with provided `name`,
|
func (c *context) Attachment(file string) (err error) {
|
||||||
// name can be empty, in that case name of the file is used.
|
f, err := os.Open(file)
|
||||||
func (c *context) File(path, name string, attachment bool) (err error) {
|
if err != nil {
|
||||||
dir, file := filepath.Split(path)
|
return
|
||||||
if attachment {
|
}
|
||||||
|
_, name := filepath.Split(file)
|
||||||
c.response.Header().Set(ContentDisposition, "attachment; filename="+name)
|
c.response.Header().Set(ContentDisposition, "attachment; filename="+name)
|
||||||
}
|
c.response.Header().Set(ContentType, c.detectContentType(file))
|
||||||
if err = c.echo.serveFile(dir, file, c); err != nil {
|
c.response.WriteHeader(http.StatusOK)
|
||||||
c.response.Header().Del(ContentDisposition)
|
_, err = io.Copy(c.response, f)
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +287,7 @@ func (c *context) NoContent(code int) error {
|
|||||||
// Redirect redirects the request using http.Redirect with status code.
|
// Redirect redirects the request using http.Redirect with status code.
|
||||||
func (c *context) Redirect(code int, url string) error {
|
func (c *context) Redirect(code int, url string) error {
|
||||||
if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
|
if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
|
||||||
return InvalidRedirectCode
|
return ErrInvalidRedirectCode
|
||||||
}
|
}
|
||||||
// TODO: v2
|
// TODO: v2
|
||||||
// http.Redirect(c.response, c.request, url, code)
|
// http.Redirect(c.response, c.request, url, code)
|
||||||
@ -295,10 +309,16 @@ func (c *context) Object() *context {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) reset(req engine.Request, res engine.Response, e *Echo) {
|
func (c *context) detectContentType(name string) (t string) {
|
||||||
|
if t = mime.TypeByExtension(filepath.Ext(name)); t == "" {
|
||||||
|
t = OctetStream
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) reset(req engine.Request, res engine.Response) {
|
||||||
c.request = req
|
c.request = req
|
||||||
c.response = res
|
c.response = res
|
||||||
c.query = nil
|
c.query = nil
|
||||||
c.store = nil
|
c.store = nil
|
||||||
c.echo = e
|
|
||||||
}
|
}
|
||||||
|
@ -157,22 +157,13 @@ func TestContext(t *testing.T) {
|
|||||||
assert.Equal(t, "Hello, <strong>World!</strong>", rec.Body.String())
|
assert.Equal(t, "Hello, <strong>World!</strong>", rec.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// File
|
// Attachment
|
||||||
rec = test.NewResponseRecorder()
|
rec = test.NewResponseRecorder()
|
||||||
c = NewContext(req, rec, e)
|
c = NewContext(req, rec, e)
|
||||||
err = c.File("_fixture/images/walle.png", "", false)
|
err = c.Attachment("_fixture/images/walle.png")
|
||||||
if assert.NoError(t, err) {
|
if assert.NoError(t, err) {
|
||||||
assert.Equal(t, http.StatusOK, rec.Status())
|
assert.Equal(t, http.StatusOK, rec.Status())
|
||||||
assert.Equal(t, 219885, rec.Body.Len())
|
assert.Equal(t, rec.Header().Get(ContentDisposition), "attachment; filename=walle.png")
|
||||||
}
|
|
||||||
|
|
||||||
// File as attachment
|
|
||||||
rec = test.NewResponseRecorder()
|
|
||||||
c = NewContext(req, rec, e)
|
|
||||||
err = c.File("_fixture/images/walle.png", "WALLE.PNG", true)
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
assert.Equal(t, http.StatusOK, rec.Status())
|
|
||||||
assert.Equal(t, rec.Header().Get(ContentDisposition), "attachment; filename=WALLE.PNG")
|
|
||||||
assert.Equal(t, 219885, rec.Body.Len())
|
assert.Equal(t, 219885, rec.Body.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +185,7 @@ func TestContext(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusInternalServerError, c.Response().Status())
|
assert.Equal(t, http.StatusInternalServerError, c.Response().Status())
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
c.Object().reset(req, test.NewResponseRecorder(), e)
|
c.Object().reset(req, test.NewResponseRecorder())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextPath(t *testing.T) {
|
func TestContextPath(t *testing.T) {
|
||||||
@ -263,7 +254,7 @@ func testBindError(t *testing.T, c Context, ct string) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if assert.IsType(t, new(HTTPError), err) {
|
if assert.IsType(t, new(HTTPError), err) {
|
||||||
assert.Equal(t, UnsupportedMediaType, err)
|
assert.Equal(t, ErrUnsupportedMediaType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
269
echo.go
269
echo.go
@ -7,8 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -24,7 +22,7 @@ import (
|
|||||||
type (
|
type (
|
||||||
Echo struct {
|
Echo struct {
|
||||||
prefix string
|
prefix string
|
||||||
middleware []MiddlewareFunc
|
middleware []Middleware
|
||||||
http2 bool
|
http2 bool
|
||||||
maxParam *int
|
maxParam *int
|
||||||
notFoundHandler HandlerFunc
|
notFoundHandler HandlerFunc
|
||||||
@ -33,8 +31,6 @@ type (
|
|||||||
renderer Renderer
|
renderer Renderer
|
||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
debug bool
|
debug bool
|
||||||
hook engine.HandlerFunc
|
|
||||||
autoIndex bool
|
|
||||||
router *Router
|
router *Router
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
@ -51,10 +47,11 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
Middleware interface {
|
Middleware interface {
|
||||||
Process(HandlerFunc) HandlerFunc
|
Handle(Handler) Handler
|
||||||
|
Priority() int
|
||||||
}
|
}
|
||||||
|
|
||||||
MiddlewareFunc func(HandlerFunc) HandlerFunc
|
MiddlewareFunc func(Handler) Handler
|
||||||
|
|
||||||
Handler interface {
|
Handler interface {
|
||||||
Handle(Context) error
|
Handle(Context) error
|
||||||
@ -122,6 +119,7 @@ const (
|
|||||||
TextPlain = "text/plain"
|
TextPlain = "text/plain"
|
||||||
TextPlainCharsetUTF8 = TextPlain + "; " + CharsetUTF8
|
TextPlainCharsetUTF8 = TextPlain + "; " + CharsetUTF8
|
||||||
MultipartForm = "multipart/form-data"
|
MultipartForm = "multipart/form-data"
|
||||||
|
OctetStream = "application/octet-stream"
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
// Charset
|
// Charset
|
||||||
@ -150,8 +148,6 @@ const (
|
|||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
WebSocket = "websocket"
|
WebSocket = "websocket"
|
||||||
|
|
||||||
indexPage = "index.html"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -171,9 +167,10 @@ var (
|
|||||||
// Errors
|
// Errors
|
||||||
//--------
|
//--------
|
||||||
|
|
||||||
UnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
|
ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
|
||||||
RendererNotRegistered = errors.New("renderer not registered")
|
ErrNotFound = NewHTTPError(http.StatusNotFound)
|
||||||
InvalidRedirectCode = errors.New("invalid redirect status code")
|
ErrRendererNotRegistered = errors.New("renderer not registered")
|
||||||
|
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
// Error handlers
|
// Error handlers
|
||||||
@ -196,6 +193,7 @@ func New() (e *Echo) {
|
|||||||
return NewContext(nil, nil, e)
|
return NewContext(nil, nil, e)
|
||||||
}
|
}
|
||||||
e.router = NewRouter(e)
|
e.router = NewRouter(e)
|
||||||
|
e.middleware = []Middleware{e.router}
|
||||||
|
|
||||||
//----------
|
//----------
|
||||||
// Defaults
|
// Defaults
|
||||||
@ -211,10 +209,14 @@ func New() (e *Echo) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f MiddlewareFunc) Process(h HandlerFunc) HandlerFunc {
|
func (f MiddlewareFunc) Handle(h Handler) Handler {
|
||||||
return f(h)
|
return f(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f MiddlewareFunc) Priority() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
func (f HandlerFunc) Handle(c Context) error {
|
func (f HandlerFunc) Handle(c Context) error {
|
||||||
return f(c)
|
return f(c)
|
||||||
}
|
}
|
||||||
@ -281,18 +283,6 @@ func (e *Echo) Debug() bool {
|
|||||||
return e.debug
|
return e.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoIndex enable/disable automatically creating an index page for the directory.
|
|
||||||
func (e *Echo) AutoIndex(on bool) {
|
|
||||||
e.autoIndex = on
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hook registers a callback which is invoked from `Echo#ServerHTTP` as the first
|
|
||||||
// statement. Hook is useful if you want to modify response/response objects even
|
|
||||||
// before it hits the router or any middleware.
|
|
||||||
func (e *Echo) Hook(h engine.HandlerFunc) {
|
|
||||||
e.hook = h
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use adds handler to the middleware chain.
|
// Use adds handler to the middleware chain.
|
||||||
func (e *Echo) Use(middleware ...interface{}) {
|
func (e *Echo) Use(middleware ...interface{}) {
|
||||||
for _, m := range middleware {
|
for _, m := range middleware {
|
||||||
@ -301,190 +291,99 @@ func (e *Echo) Use(middleware ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect adds a CONNECT route > handler to the router.
|
// Connect adds a CONNECT route > handler to the router.
|
||||||
func (e *Echo) Connect(path string, handler interface{}) {
|
func (e *Echo) Connect(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(CONNECT, path, handler)
|
e.add(CONNECT, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete adds a DELETE route > handler to the router.
|
// Delete adds a DELETE route > handler to the router.
|
||||||
func (e *Echo) Delete(path string, handler interface{}) {
|
func (e *Echo) Delete(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(DELETE, path, handler)
|
e.add(DELETE, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get adds a GET route > handler to the router.
|
// Get adds a GET route > handler to the router.
|
||||||
func (e *Echo) Get(path string, handler interface{}) {
|
func (e *Echo) Get(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(GET, path, handler)
|
e.add(GET, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Head adds a HEAD route > handler to the router.
|
// Head adds a HEAD route > handler to the router.
|
||||||
func (e *Echo) Head(path string, handler interface{}) {
|
func (e *Echo) Head(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(HEAD, path, handler)
|
e.add(HEAD, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options adds an OPTIONS route > handler to the router.
|
// Options adds an OPTIONS route > handler to the router.
|
||||||
func (e *Echo) Options(path string, handler interface{}) {
|
func (e *Echo) Options(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(OPTIONS, path, handler)
|
e.add(OPTIONS, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch adds a PATCH route > handler to the router.
|
// Patch adds a PATCH route > handler to the router.
|
||||||
func (e *Echo) Patch(path string, handler interface{}) {
|
func (e *Echo) Patch(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(PATCH, path, handler)
|
e.add(PATCH, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post adds a POST route > handler to the router.
|
// Post adds a POST route > handler to the router.
|
||||||
func (e *Echo) Post(path string, handler interface{}) {
|
func (e *Echo) Post(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(POST, path, handler)
|
e.add(POST, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put adds a PUT route > handler to the router.
|
// Put adds a PUT route > handler to the router.
|
||||||
func (e *Echo) Put(path string, handler interface{}) {
|
func (e *Echo) Put(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(PUT, path, handler)
|
e.add(PUT, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trace adds a TRACE route > handler to the router.
|
// Trace adds a TRACE route > handler to the router.
|
||||||
func (e *Echo) Trace(path string, handler interface{}) {
|
func (e *Echo) Trace(path string, handler interface{}, middleware ...interface{}) {
|
||||||
e.add(TRACE, path, handler)
|
e.add(TRACE, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any adds a route > handler to the router for all HTTP methods.
|
// Any adds a route > handler to the router for all HTTP methods.
|
||||||
func (e *Echo) Any(path string, handler interface{}) {
|
func (e *Echo) Any(path string, handler interface{}, middleware ...interface{}) {
|
||||||
for _, m := range methods {
|
for _, m := range methods {
|
||||||
e.add(m, path, handler)
|
e.add(m, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match adds a route > handler to the router for multiple HTTP methods provided.
|
// Match adds a route > handler to the router for multiple HTTP methods provided.
|
||||||
func (e *Echo) Match(methods []string, path string, handler interface{}) {
|
func (e *Echo) Match(methods []string, path string, handler interface{}, middleware ...interface{}) {
|
||||||
for _, m := range methods {
|
for _, m := range methods {
|
||||||
e.add(m, path, handler)
|
e.add(m, path, handler, middleware...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: v2
|
// NOTE: v2
|
||||||
func (e *Echo) add(method, path string, h interface{}) {
|
func (e *Echo) add(method, path string, handler interface{}, middleware ...interface{}) {
|
||||||
path = e.prefix + path
|
h := wrapHandler(handler)
|
||||||
e.router.Add(method, path, wrapHandler(h), e)
|
name := handlerName(handler)
|
||||||
|
e.router.Add(method, path, HandlerFunc(func(c Context) error {
|
||||||
|
for _, m := range middleware {
|
||||||
|
h = wrapMiddleware(m).Handle(h)
|
||||||
|
}
|
||||||
|
return h.Handle(c)
|
||||||
|
}), e)
|
||||||
r := Route{
|
r := Route{
|
||||||
Method: method,
|
Method: method,
|
||||||
Path: path,
|
Path: path,
|
||||||
Handler: runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name(),
|
Handler: name,
|
||||||
}
|
}
|
||||||
e.router.routes = append(e.router.routes, r)
|
e.router.routes = append(e.router.routes, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index serves index file.
|
// Group creates a new sub-router with prefix.
|
||||||
func (e *Echo) Index(file string) {
|
func (e *Echo) Group(prefix string, middleware ...interface{}) (g *Group) {
|
||||||
e.ServeFile("/", file)
|
g = &Group{prefix: prefix, echo: e}
|
||||||
}
|
g.Use(middleware...)
|
||||||
|
|
||||||
// Favicon serves the default favicon - GET /favicon.ico.
|
|
||||||
func (e *Echo) Favicon(file string) {
|
|
||||||
e.ServeFile("/favicon.ico", file)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static serves static files from a directory. It's an alias for `Echo.ServeDir`
|
|
||||||
func (e *Echo) Static(path, dir string) {
|
|
||||||
e.ServeDir(path, dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeDir serves files from a directory.
|
|
||||||
func (e *Echo) ServeDir(path, dir string) {
|
|
||||||
e.Get(path+"*", func(c Context) error {
|
|
||||||
return e.serveFile(dir, c.P(0), c) // Param `_*`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeFile serves a file.
|
|
||||||
func (e *Echo) ServeFile(path, file string) {
|
|
||||||
e.Get(path, func(c Context) error {
|
|
||||||
dir, file := filepath.Split(file)
|
|
||||||
return e.serveFile(dir, file, c)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Echo) serveFile(dir, file string, c Context) (err error) {
|
|
||||||
fs := http.Dir(dir)
|
|
||||||
f, err := fs.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
return NewHTTPError(http.StatusNotFound)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
fi, _ := f.Stat()
|
|
||||||
if fi.IsDir() {
|
|
||||||
/* NOTE:
|
|
||||||
Not checking the Last-Modified header as it caches the response `304` when
|
|
||||||
changing differnt directories for the same path.
|
|
||||||
*/
|
|
||||||
d := f
|
|
||||||
|
|
||||||
// Index file
|
|
||||||
file = path.Join(file, indexPage)
|
|
||||||
f, err = fs.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
if e.autoIndex {
|
|
||||||
// Auto index
|
|
||||||
return listDir(d, c)
|
|
||||||
}
|
|
||||||
return NewHTTPError(http.StatusForbidden)
|
|
||||||
}
|
|
||||||
fi, _ = f.Stat() // Index file stat
|
|
||||||
}
|
|
||||||
c.Response().WriteHeader(http.StatusOK)
|
|
||||||
io.Copy(c.Response(), f)
|
|
||||||
// TODO:
|
|
||||||
// http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func listDir(d http.File, c Context) (err error) {
|
|
||||||
dirs, err := d.Readdir(-1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create directory index
|
|
||||||
w := c.Response()
|
|
||||||
w.Header().Set(ContentType, TextHTMLCharsetUTF8)
|
|
||||||
fmt.Fprintf(w, "<pre>\n")
|
|
||||||
for _, d := range dirs {
|
|
||||||
name := d.Name()
|
|
||||||
color := "#212121"
|
|
||||||
if d.IsDir() {
|
|
||||||
color = "#e91e63"
|
|
||||||
name += "/"
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "</pre>\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group creates a new sub router with prefix. It inherits all properties from
|
|
||||||
// the parent. Passing middleware overrides parent middleware.
|
|
||||||
func (e *Echo) Group(prefix string, m ...MiddlewareFunc) *Group {
|
|
||||||
g := &Group{*e}
|
|
||||||
g.echo.prefix += prefix
|
|
||||||
if len(m) == 0 {
|
|
||||||
mw := make([]MiddlewareFunc, len(g.echo.middleware))
|
|
||||||
copy(mw, g.echo.middleware)
|
|
||||||
g.echo.middleware = mw
|
|
||||||
} else {
|
|
||||||
g.echo.middleware = nil
|
|
||||||
g.Use(m...)
|
|
||||||
}
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
|
|
||||||
// URI generates a URI from handler.
|
// URI generates a URI from handler.
|
||||||
func (e *Echo) URI(h HandlerFunc, params ...interface{}) string {
|
func (e *Echo) URI(handler interface{}, params ...interface{}) string {
|
||||||
uri := new(bytes.Buffer)
|
uri := new(bytes.Buffer)
|
||||||
pl := len(params)
|
ln := len(params)
|
||||||
n := 0
|
n := 0
|
||||||
hn := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
|
name := handlerName(handler)
|
||||||
for _, r := range e.router.routes {
|
for _, r := range e.router.routes {
|
||||||
if r.Handler == hn {
|
if r.Handler == name {
|
||||||
for i, l := 0, len(r.Path); i < l; i++ {
|
for i, l := 0, len(r.Path); i < l; i++ {
|
||||||
if r.Path[i] == ':' && n < pl {
|
if r.Path[i] == ':' && n < ln {
|
||||||
for ; i < l && r.Path[i] != '/'; i++ {
|
for ; i < l && r.Path[i] != '/'; i++ {
|
||||||
}
|
}
|
||||||
uri.WriteString(fmt.Sprintf("%v", params[n]))
|
uri.WriteString(fmt.Sprintf("%v", params[n]))
|
||||||
@ -501,8 +400,8 @@ func (e *Echo) URI(h HandlerFunc, params ...interface{}) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// URL is an alias for `URI` function.
|
// URL is an alias for `URI` function.
|
||||||
func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
|
func (e *Echo) URL(handler interface{}, params ...interface{}) string {
|
||||||
return e.URI(h, params...)
|
return e.URI(handler, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routes returns the registered routes.
|
// Routes returns the registered routes.
|
||||||
@ -511,37 +410,23 @@ func (e *Echo) Routes() []Route {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Echo) ServeHTTP(req engine.Request, res engine.Response) {
|
func (e *Echo) ServeHTTP(req engine.Request, res engine.Response) {
|
||||||
if e.hook != nil {
|
|
||||||
e.hook(req, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := e.pool.Get().(*context)
|
c := e.pool.Get().(*context)
|
||||||
h, e := e.router.Find(req.Method(), req.URL().Path(), c)
|
c.reset(req, res)
|
||||||
c.reset(req, res, e)
|
h := Handler(c)
|
||||||
|
|
||||||
// Chain middleware with handler in the end
|
// Chain middleware with handler in the end
|
||||||
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].Handle(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute chain
|
// Execute chain
|
||||||
if err := h(c); err != nil {
|
if err := h.Handle(c); err != nil {
|
||||||
e.httpErrorHandler(err, c)
|
e.httpErrorHandler(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.pool.Put(c)
|
e.pool.Put(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server returns the internal *http.Server.
|
|
||||||
// func (e *Echo) Server(addr string) *http.Server {
|
|
||||||
// s := &http.Server{Addr: addr, Handler: e}
|
|
||||||
// // TODO: Remove in Go 1.6+
|
|
||||||
// if e.http2 {
|
|
||||||
// http2.ConfigureServer(s, nil)
|
|
||||||
// }
|
|
||||||
// return s
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Run starts the HTTP engine.
|
// Run starts the HTTP engine.
|
||||||
func (e *Echo) Run(eng engine.Engine) {
|
func (e *Echo) Run(eng engine.Engine) {
|
||||||
eng.SetHandler(e.ServeHTTP)
|
eng.SetHandler(e.ServeHTTP)
|
||||||
@ -575,7 +460,7 @@ func (e *HTTPError) Error() string {
|
|||||||
|
|
||||||
func (binder) Bind(r engine.Request, i interface{}) (err error) {
|
func (binder) Bind(r engine.Request, i interface{}) (err error) {
|
||||||
ct := r.Header().Get(ContentType)
|
ct := r.Header().Get(ContentType)
|
||||||
err = UnsupportedMediaType
|
err = ErrUnsupportedMediaType
|
||||||
if strings.HasPrefix(ct, ApplicationJSON) {
|
if strings.HasPrefix(ct, ApplicationJSON) {
|
||||||
if err = json.NewDecoder(r.Body()).Decode(i); err != nil {
|
if err = json.NewDecoder(r.Body()).Decode(i); err != nil {
|
||||||
err = NewHTTPError(http.StatusBadRequest, err.Error())
|
err = NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
@ -588,28 +473,40 @@ func (binder) Bind(r engine.Request, i interface{}) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapMiddleware(m interface{}) MiddlewareFunc {
|
func wrapMiddleware(m interface{}) Middleware {
|
||||||
switch m := m.(type) {
|
switch m := m.(type) {
|
||||||
case Middleware:
|
case Middleware:
|
||||||
return m.Process
|
return m
|
||||||
case MiddlewareFunc:
|
case MiddlewareFunc:
|
||||||
return m
|
return m
|
||||||
case func(HandlerFunc) HandlerFunc:
|
case func(Handler) Handler:
|
||||||
return m
|
return MiddlewareFunc(m)
|
||||||
default:
|
default:
|
||||||
panic("invalid middleware")
|
panic("invalid middleware")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapHandler(h interface{}) HandlerFunc {
|
func wrapHandler(h interface{}) Handler {
|
||||||
switch h := h.(type) {
|
switch h := h.(type) {
|
||||||
case Handler:
|
case Handler:
|
||||||
return h.Handle
|
return h
|
||||||
case HandlerFunc:
|
case HandlerFunc:
|
||||||
return h
|
return h
|
||||||
case func(Context) error:
|
case func(Context) error:
|
||||||
return h
|
return HandlerFunc(h)
|
||||||
default:
|
default:
|
||||||
panic("invalid handler")
|
panic("echo => invalid handler")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerName(h interface{}) string {
|
||||||
|
switch h := h.(type) {
|
||||||
|
case Handler:
|
||||||
|
t := reflect.TypeOf(h)
|
||||||
|
return fmt.Sprintf("%s » %s", t.PkgPath(), t.Name())
|
||||||
|
case HandlerFunc, func(Context) error:
|
||||||
|
return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
|
||||||
|
default:
|
||||||
|
panic("echo => invalid handler")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
145
echo_test.go
145
echo_test.go
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/labstack/echo/engine"
|
|
||||||
"github.com/labstack/echo/test"
|
"github.com/labstack/echo/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -41,77 +40,29 @@ func TestEcho(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusInternalServerError, rec.Status())
|
assert.Equal(t, http.StatusInternalServerError, rec.Status())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEchoIndex(t *testing.T) {
|
|
||||||
e := New()
|
|
||||||
e.Index("_fixture/index.html")
|
|
||||||
c, b := request(GET, "/", e)
|
|
||||||
assert.Equal(t, http.StatusOK, c)
|
|
||||||
assert.NotEmpty(t, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEchoFavicon(t *testing.T) {
|
|
||||||
e := New()
|
|
||||||
e.Favicon("_fixture/favicon.ico")
|
|
||||||
c, b := request(GET, "/favicon.ico", e)
|
|
||||||
assert.Equal(t, http.StatusOK, c)
|
|
||||||
assert.NotEmpty(t, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEchoStatic(t *testing.T) {
|
|
||||||
e := New()
|
|
||||||
|
|
||||||
// OK
|
|
||||||
e.Static("/images", "_fixture/images")
|
|
||||||
c, b := request(GET, "/images/walle.png", e)
|
|
||||||
assert.Equal(t, http.StatusOK, c)
|
|
||||||
assert.NotEmpty(t, b)
|
|
||||||
|
|
||||||
// No file
|
|
||||||
e.Static("/images", "_fixture/scripts")
|
|
||||||
c, _ = request(GET, "/images/bolt.png", e)
|
|
||||||
assert.Equal(t, http.StatusNotFound, c)
|
|
||||||
|
|
||||||
// Directory
|
|
||||||
e.Static("/images", "_fixture/images")
|
|
||||||
c, _ = request(GET, "/images", e)
|
|
||||||
assert.Equal(t, http.StatusForbidden, c)
|
|
||||||
|
|
||||||
// Directory with index.html
|
|
||||||
e.Static("/", "_fixture")
|
|
||||||
c, r := request(GET, "/", e)
|
|
||||||
assert.Equal(t, http.StatusOK, c)
|
|
||||||
assert.Equal(t, true, strings.HasPrefix(r, "<!doctype html>"))
|
|
||||||
|
|
||||||
// Sub-directory with index.html
|
|
||||||
c, r = request(GET, "/folder", e)
|
|
||||||
assert.Equal(t, http.StatusOK, c)
|
|
||||||
assert.Equal(t, true, strings.HasPrefix(r, "<!doctype html>"))
|
|
||||||
// assert.Equal(t, "sub directory", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEchoMiddleware(t *testing.T) {
|
func TestEchoMiddleware(t *testing.T) {
|
||||||
e := New()
|
e := New()
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
e.Use(func(h HandlerFunc) HandlerFunc {
|
e.Use(func(h Handler) Handler {
|
||||||
return func(c Context) error {
|
return HandlerFunc(func(c Context) error {
|
||||||
buf.WriteString("a")
|
buf.WriteString("a")
|
||||||
return h(c)
|
return h.Handle(c)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
e.Use(func(h HandlerFunc) HandlerFunc {
|
e.Use(func(h Handler) Handler {
|
||||||
return func(c Context) error {
|
return HandlerFunc(func(c Context) error {
|
||||||
buf.WriteString("b")
|
buf.WriteString("b")
|
||||||
return h(c)
|
return h.Handle(c)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
e.Use(func(h HandlerFunc) HandlerFunc {
|
e.Use(func(h Handler) Handler {
|
||||||
return func(c Context) error {
|
return HandlerFunc(func(c Context) error {
|
||||||
buf.WriteString("c")
|
buf.WriteString("c")
|
||||||
return h(c)
|
return h.Handle(c)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Route
|
// Route
|
||||||
@ -125,10 +76,10 @@ func TestEchoMiddleware(t *testing.T) {
|
|||||||
assert.Equal(t, "OK", b)
|
assert.Equal(t, "OK", b)
|
||||||
|
|
||||||
// Error
|
// Error
|
||||||
e.Use(func(h HandlerFunc) HandlerFunc {
|
e.Use(func(Handler) Handler {
|
||||||
return func(c Context) error {
|
return HandlerFunc(func(c Context) error {
|
||||||
return errors.New("error")
|
return errors.New("error")
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
c, b = request(GET, "/", e)
|
c, b = request(GET, "/", e)
|
||||||
assert.Equal(t, http.StatusInternalServerError, c)
|
assert.Equal(t, http.StatusInternalServerError, c)
|
||||||
@ -138,9 +89,9 @@ func TestEchoHandler(t *testing.T) {
|
|||||||
e := New()
|
e := New()
|
||||||
|
|
||||||
// HandlerFunc
|
// HandlerFunc
|
||||||
e.Get("/ok", HandlerFunc(func(c Context) error {
|
e.Get("/ok", func(c Context) error {
|
||||||
return c.String(http.StatusOK, "OK")
|
return c.String(http.StatusOK, "OK")
|
||||||
}))
|
})
|
||||||
|
|
||||||
c, b := request(GET, "/ok", e)
|
c, b := request(GET, "/ok", e)
|
||||||
assert.Equal(t, http.StatusOK, c)
|
assert.Equal(t, http.StatusOK, c)
|
||||||
@ -208,7 +159,6 @@ func TestEchoMatch(t *testing.T) { // JFC
|
|||||||
|
|
||||||
func TestEchoURL(t *testing.T) {
|
func TestEchoURL(t *testing.T) {
|
||||||
e := New()
|
e := New()
|
||||||
|
|
||||||
static := func(Context) error { return nil }
|
static := func(Context) error { return nil }
|
||||||
getUser := func(Context) error { return nil }
|
getUser := func(Context) error { return nil }
|
||||||
getFile := func(Context) error { return nil }
|
getFile := func(Context) error { return nil }
|
||||||
@ -248,11 +198,11 @@ func TestEchoRoutes(t *testing.T) {
|
|||||||
func TestEchoGroup(t *testing.T) {
|
func TestEchoGroup(t *testing.T) {
|
||||||
e := New()
|
e := New()
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
e.Use(func(h HandlerFunc) HandlerFunc {
|
e.Use(func(h Handler) Handler {
|
||||||
return func(c Context) error {
|
return HandlerFunc(func(c Context) error {
|
||||||
buf.WriteString("0")
|
buf.WriteString("0")
|
||||||
return h(c)
|
return h.Handle(c)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
h := func(c Context) error {
|
h := func(c Context) error {
|
||||||
return c.NoContent(http.StatusOK)
|
return c.NoContent(http.StatusOK)
|
||||||
@ -266,27 +216,18 @@ func TestEchoGroup(t *testing.T) {
|
|||||||
|
|
||||||
// Group
|
// Group
|
||||||
g1 := e.Group("/group1")
|
g1 := e.Group("/group1")
|
||||||
g1.Use(func(h HandlerFunc) HandlerFunc {
|
g1.Use(func(h Handler) Handler {
|
||||||
return func(c Context) error {
|
return HandlerFunc(func(c Context) error {
|
||||||
buf.WriteString("1")
|
buf.WriteString("1")
|
||||||
return h(c)
|
return h.Handle(c)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
g1.Get("/", h)
|
g1.Get("/", h)
|
||||||
|
|
||||||
// Group with no parent middleware
|
|
||||||
g2 := e.Group("/group2", func(h HandlerFunc) HandlerFunc {
|
|
||||||
return func(c Context) error {
|
|
||||||
buf.WriteString("2")
|
|
||||||
return h(c)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
g2.Get("/", h)
|
|
||||||
|
|
||||||
// Nested groups
|
// Nested groups
|
||||||
g3 := e.Group("/group3")
|
g2 := e.Group("/group2")
|
||||||
g4 := g3.Group("/group4")
|
g3 := g2.Group("/group3")
|
||||||
g4.Get("/", h)
|
g3.Get("/", h)
|
||||||
|
|
||||||
request(GET, "/users", e)
|
request(GET, "/users", e)
|
||||||
assert.Equal(t, "0", buf.String())
|
assert.Equal(t, "0", buf.String())
|
||||||
@ -296,11 +237,7 @@ func TestEchoGroup(t *testing.T) {
|
|||||||
assert.Equal(t, "01", buf.String())
|
assert.Equal(t, "01", buf.String())
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
request(GET, "/group2/", e)
|
c, _ := request(GET, "/group2/group3/", e)
|
||||||
assert.Equal(t, "2", buf.String())
|
|
||||||
|
|
||||||
buf.Reset()
|
|
||||||
c, _ := request(GET, "/group3/group4/", e)
|
|
||||||
assert.Equal(t, http.StatusOK, c)
|
assert.Equal(t, http.StatusOK, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,30 +267,6 @@ func TestEchoHTTPError(t *testing.T) {
|
|||||||
assert.Equal(t, m, he.Error())
|
assert.Equal(t, m, he.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEchoServer(t *testing.T) {
|
|
||||||
// e := New()
|
|
||||||
// s := e.Server(":1323")
|
|
||||||
// assert.IsType(t, &http.Server{}, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEchoHook(t *testing.T) {
|
|
||||||
e := New()
|
|
||||||
e.Get("/test", func(c Context) error {
|
|
||||||
return c.NoContent(http.StatusNoContent)
|
|
||||||
})
|
|
||||||
e.Hook(func(req engine.Request, res engine.Response) {
|
|
||||||
path := req.URL().Path()
|
|
||||||
l := len(path) - 1
|
|
||||||
if path != "/" && path[l] == '/' {
|
|
||||||
req.URL().SetPath(path[:l])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
req := test.NewRequest(GET, "/test/", nil)
|
|
||||||
rec := test.NewResponseRecorder()
|
|
||||||
e.ServeHTTP(req, rec)
|
|
||||||
assert.Equal(t, req.URL().Path(), "/test")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testMethod(t *testing.T, method, path string, e *Echo) {
|
func testMethod(t *testing.T, method, path string, e *Echo) {
|
||||||
m := fmt.Sprintf("%c%s", method[0], strings.ToLower(method[1:]))
|
m := fmt.Sprintf("%c%s", method[0], strings.ToLower(method[1:]))
|
||||||
p := reflect.ValueOf(path)
|
p := reflect.ValueOf(path)
|
||||||
|
76
group.go
76
group.go
@ -2,64 +2,72 @@ package echo
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
Group struct {
|
Group struct {
|
||||||
echo Echo
|
prefix string
|
||||||
|
middleware []Middleware
|
||||||
|
echo *Echo
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *Group) Use(m ...MiddlewareFunc) {
|
func (g *Group) Use(middleware ...interface{}) {
|
||||||
for _, h := range m {
|
for _, m := range middleware {
|
||||||
g.echo.middleware = append(g.echo.middleware, h)
|
g.middleware = append(g.middleware, wrapMiddleware(m))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Connect(path string, h HandlerFunc) {
|
func (g *Group) Connect(path string, handler interface{}) {
|
||||||
g.echo.Connect(path, h)
|
g.add(CONNECT, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Delete(path string, h HandlerFunc) {
|
func (g *Group) Delete(path string, handler interface{}) {
|
||||||
g.echo.Delete(path, h)
|
g.add(DELETE, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Get(path string, h HandlerFunc) {
|
func (g *Group) Get(path string, handler interface{}) {
|
||||||
g.echo.Get(path, h)
|
g.add(GET, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Head(path string, h HandlerFunc) {
|
func (g *Group) Head(path string, handler interface{}) {
|
||||||
g.echo.Head(path, h)
|
g.add(HEAD, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Options(path string, h HandlerFunc) {
|
func (g *Group) Options(path string, handler interface{}) {
|
||||||
g.echo.Options(path, h)
|
g.add(OPTIONS, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Patch(path string, h HandlerFunc) {
|
func (g *Group) Patch(path string, handler interface{}) {
|
||||||
g.echo.Patch(path, h)
|
g.add(PATCH, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Post(path string, h HandlerFunc) {
|
func (g *Group) Post(path string, handler interface{}) {
|
||||||
g.echo.Post(path, h)
|
g.add(POST, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Put(path string, h HandlerFunc) {
|
func (g *Group) Put(path string, handler interface{}) {
|
||||||
g.echo.Put(path, h)
|
g.add(PUT, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Trace(path string, h HandlerFunc) {
|
func (g *Group) Trace(path string, handler interface{}) {
|
||||||
g.echo.Trace(path, h)
|
g.add(TRACE, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) Static(path, root string) {
|
func (g *Group) Group(prefix string, middleware ...interface{}) *Group {
|
||||||
g.echo.Static(path, root)
|
return g.echo.Group(prefix, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Group) ServeDir(path, root string) {
|
func (g *Group) add(method, path string, handler interface{}) {
|
||||||
g.echo.ServeDir(path, root)
|
path = g.prefix + path
|
||||||
}
|
h := wrapHandler(handler)
|
||||||
|
name := handlerName(handler)
|
||||||
func (g *Group) ServeFile(path, file string) {
|
g.echo.router.Add(method, path, HandlerFunc(func(c Context) error {
|
||||||
g.echo.ServeFile(path, file)
|
for i := len(g.middleware) - 1; i >= 0; i-- {
|
||||||
}
|
h = g.middleware[i].Handle(h)
|
||||||
|
}
|
||||||
func (g *Group) Group(prefix string, m ...MiddlewareFunc) *Group {
|
return h.Handle(c)
|
||||||
return g.echo.Group(prefix, m...)
|
}), g.echo)
|
||||||
|
r := Route{
|
||||||
|
Method: method,
|
||||||
|
Path: path,
|
||||||
|
Handler: name,
|
||||||
|
}
|
||||||
|
g.echo.router.routes = append(g.echo.router.routes, r)
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,4 @@ func TestGroup(t *testing.T) {
|
|||||||
g.Post("/", h)
|
g.Post("/", h)
|
||||||
g.Put("/", h)
|
g.Put("/", h)
|
||||||
g.Trace("/", h)
|
g.Trace("/", h)
|
||||||
g.Static("/scripts", "scripts")
|
|
||||||
g.ServeDir("/scripts", "scripts")
|
|
||||||
g.ServeFile("/scripts/main.js", "scripts/main.js")
|
|
||||||
}
|
}
|
||||||
|
92
handler/static.go
Normal file
92
handler/static.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Static struct {
|
||||||
|
Root string
|
||||||
|
Browse bool
|
||||||
|
Index string
|
||||||
|
}
|
||||||
|
|
||||||
|
FaviconOptions struct {
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Static) Handle(c echo.Context) error {
|
||||||
|
fs := http.Dir(s.Root)
|
||||||
|
file := c.P(0)
|
||||||
|
f, err := fs.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return echo.ErrNotFound
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.IsDir() {
|
||||||
|
/* NOTE:
|
||||||
|
Not checking the Last-Modified header as it caches the response `304` when
|
||||||
|
changing differnt directories for the same path.
|
||||||
|
*/
|
||||||
|
d := f
|
||||||
|
|
||||||
|
// Index file
|
||||||
|
file = path.Join(file, s.Index)
|
||||||
|
f, err = fs.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
if s.Browse {
|
||||||
|
dirs, err := d.Readdir(-1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a directory index
|
||||||
|
w := c.Response()
|
||||||
|
w.Header().Set(echo.ContentType, echo.TextHTMLCharsetUTF8)
|
||||||
|
if _, err = fmt.Fprintf(w, "<pre>\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, d := range dirs {
|
||||||
|
name := d.Name()
|
||||||
|
color := "#212121"
|
||||||
|
if d.IsDir() {
|
||||||
|
color = "#e91e63"
|
||||||
|
name += "/"
|
||||||
|
}
|
||||||
|
if _, err = fmt.Fprintf(w, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintf(w, "</pre>\n")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return echo.ErrNotFound
|
||||||
|
}
|
||||||
|
fi, _ = f.Stat() // Index file stat
|
||||||
|
}
|
||||||
|
c.Response().WriteHeader(http.StatusOK)
|
||||||
|
io.Copy(c.Response(), f)
|
||||||
|
return nil
|
||||||
|
// TODO:
|
||||||
|
// http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Favicon serves the default favicon - GET /favicon.ico.
|
||||||
|
func Favicon(root string, options ...FaviconOptions) echo.MiddlewareFunc {
|
||||||
|
return func(h echo.Handler) echo.Handler {
|
||||||
|
return echo.HandlerFunc(func(c echo.Context) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -20,8 +20,8 @@ const (
|
|||||||
// For valid credentials it calls the next handler.
|
// For valid credentials it calls the next handler.
|
||||||
// For invalid credentials, it sends "401 - Unauthorized" response.
|
// For invalid credentials, it sends "401 - Unauthorized" response.
|
||||||
func BasicAuth(fn BasicValidateFunc) echo.MiddlewareFunc {
|
func BasicAuth(fn BasicValidateFunc) echo.MiddlewareFunc {
|
||||||
return func(h echo.HandlerFunc) echo.HandlerFunc {
|
return func(h echo.Handler) echo.Handler {
|
||||||
return func(c echo.Context) error {
|
return echo.HandlerFunc(func(c echo.Context) error {
|
||||||
// Skip WebSocket
|
// Skip WebSocket
|
||||||
if (c.Request().Header().Get(echo.Upgrade)) == echo.WebSocket {
|
if (c.Request().Header().Get(echo.Upgrade)) == echo.WebSocket {
|
||||||
return nil
|
return nil
|
||||||
@ -46,6 +46,6 @@ func BasicAuth(fn BasicValidateFunc) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
c.Response().Header().Set(echo.WWWAuthenticate, basic+" realm=Restricted")
|
c.Response().Header().Set(echo.WWWAuthenticate, basic+" realm=Restricted")
|
||||||
return echo.NewHTTPError(http.StatusUnauthorized)
|
return echo.NewHTTPError(http.StatusUnauthorized)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,15 +21,14 @@ func TestBasicAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
h := func(c echo.Context) error {
|
h := BasicAuth(fn)(echo.HandlerFunc(func(c echo.Context) error {
|
||||||
return c.String(http.StatusOK, "test")
|
return c.String(http.StatusOK, "test")
|
||||||
}
|
}))
|
||||||
mw := BasicAuth(fn)(h)
|
|
||||||
|
|
||||||
// Valid credentials
|
// Valid credentials
|
||||||
auth := basic + " " + base64.StdEncoding.EncodeToString([]byte("joe:secret"))
|
auth := basic + " " + base64.StdEncoding.EncodeToString([]byte("joe:secret"))
|
||||||
req.Header().Set(echo.Authorization, auth)
|
req.Header().Set(echo.Authorization, auth)
|
||||||
assert.NoError(t, mw(c))
|
assert.NoError(t, h.Handle(c))
|
||||||
|
|
||||||
//---------------------
|
//---------------------
|
||||||
// Invalid credentials
|
// Invalid credentials
|
||||||
@ -38,24 +37,24 @@ func TestBasicAuth(t *testing.T) {
|
|||||||
// Incorrect password
|
// Incorrect password
|
||||||
auth = basic + " " + base64.StdEncoding.EncodeToString([]byte("joe:password"))
|
auth = basic + " " + base64.StdEncoding.EncodeToString([]byte("joe:password"))
|
||||||
req.Header().Set(echo.Authorization, auth)
|
req.Header().Set(echo.Authorization, auth)
|
||||||
he := mw(c).(*echo.HTTPError)
|
he := h.Handle(c).(*echo.HTTPError)
|
||||||
assert.Equal(t, http.StatusUnauthorized, he.Code())
|
assert.Equal(t, http.StatusUnauthorized, he.Code())
|
||||||
assert.Equal(t, basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
|
assert.Equal(t, basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
|
||||||
|
|
||||||
// Empty Authorization header
|
// Empty Authorization header
|
||||||
req.Header().Set(echo.Authorization, "")
|
req.Header().Set(echo.Authorization, "")
|
||||||
he = mw(c).(*echo.HTTPError)
|
he = h.Handle(c).(*echo.HTTPError)
|
||||||
assert.Equal(t, http.StatusUnauthorized, he.Code())
|
assert.Equal(t, http.StatusUnauthorized, he.Code())
|
||||||
assert.Equal(t, basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
|
assert.Equal(t, basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
|
||||||
|
|
||||||
// Invalid Authorization header
|
// Invalid Authorization header
|
||||||
auth = base64.StdEncoding.EncodeToString([]byte("invalid"))
|
auth = base64.StdEncoding.EncodeToString([]byte("invalid"))
|
||||||
req.Header().Set(echo.Authorization, auth)
|
req.Header().Set(echo.Authorization, auth)
|
||||||
he = mw(c).(*echo.HTTPError)
|
he = h.Handle(c).(*echo.HTTPError)
|
||||||
assert.Equal(t, http.StatusUnauthorized, he.Code())
|
assert.Equal(t, http.StatusUnauthorized, he.Code())
|
||||||
assert.Equal(t, basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
|
assert.Equal(t, basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
|
||||||
|
|
||||||
// WebSocket
|
// WebSocket
|
||||||
c.Request().Header().Set(echo.Upgrade, echo.WebSocket)
|
c.Request().Header().Set(echo.Upgrade, echo.WebSocket)
|
||||||
assert.NoError(t, mw(c))
|
assert.NoError(t, h.Handle(c))
|
||||||
}
|
}
|
||||||
|
@ -49,10 +49,9 @@ var writerPool = sync.Pool{
|
|||||||
// Gzip returns a middleware which compresses HTTP response using gzip compression
|
// Gzip returns a middleware which compresses HTTP response using gzip compression
|
||||||
// scheme.
|
// scheme.
|
||||||
func Gzip() echo.MiddlewareFunc {
|
func Gzip() echo.MiddlewareFunc {
|
||||||
return func(h echo.HandlerFunc) echo.HandlerFunc {
|
return func(h echo.Handler) echo.Handler {
|
||||||
scheme := "gzip"
|
scheme := "gzip"
|
||||||
|
return echo.HandlerFunc(func(c echo.Context) error {
|
||||||
return func(c echo.Context) error {
|
|
||||||
c.Response().Header().Add(echo.Vary, echo.AcceptEncoding)
|
c.Response().Header().Add(echo.Vary, echo.AcceptEncoding)
|
||||||
if strings.Contains(c.Request().Header().Get(echo.AcceptEncoding), scheme) {
|
if strings.Contains(c.Request().Header().Get(echo.AcceptEncoding), scheme) {
|
||||||
w := writerPool.Get().(*gzip.Writer)
|
w := writerPool.Get().(*gzip.Writer)
|
||||||
@ -69,6 +68,6 @@ func Gzip() echo.MiddlewareFunc {
|
|||||||
c.Error(err)
|
c.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,13 +35,12 @@ func TestGzip(t *testing.T) {
|
|||||||
req := test.NewRequest(echo.GET, "/", nil)
|
req := test.NewRequest(echo.GET, "/", nil)
|
||||||
rec := test.NewResponseRecorder()
|
rec := test.NewResponseRecorder()
|
||||||
c := echo.NewContext(req, rec, e)
|
c := echo.NewContext(req, rec, e)
|
||||||
h := func(c echo.Context) error {
|
// Skip if no Accept-Encoding header
|
||||||
|
h := Gzip()(echo.HandlerFunc(func(c echo.Context) error {
|
||||||
c.Response().Write([]byte("test")) // For Content-Type sniffing
|
c.Response().Write([]byte("test")) // For Content-Type sniffing
|
||||||
return nil
|
return nil
|
||||||
}
|
}))
|
||||||
|
h.Handle(c)
|
||||||
// Skip if no Accept-Encoding header
|
|
||||||
Gzip()(h)(c)
|
|
||||||
// assert.Equal(t, http.StatusOK, rec.Status())
|
// assert.Equal(t, http.StatusOK, rec.Status())
|
||||||
assert.Equal(t, "test", rec.Body.String())
|
assert.Equal(t, "test", rec.Body.String())
|
||||||
|
|
||||||
@ -51,7 +50,7 @@ func TestGzip(t *testing.T) {
|
|||||||
c = echo.NewContext(req, rec, e)
|
c = echo.NewContext(req, rec, e)
|
||||||
|
|
||||||
// Gzip
|
// Gzip
|
||||||
Gzip()(h)(c)
|
h.Handle(c)
|
||||||
// assert.Equal(t, http.StatusOK, rec.Status())
|
// assert.Equal(t, http.StatusOK, rec.Status())
|
||||||
assert.Equal(t, "gzip", rec.Header().Get(echo.ContentEncoding))
|
assert.Equal(t, "gzip", rec.Header().Get(echo.ContentEncoding))
|
||||||
assert.Contains(t, rec.Header().Get(echo.ContentType), echo.TextPlain)
|
assert.Contains(t, rec.Header().Get(echo.ContentType), echo.TextPlain)
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Log() echo.MiddlewareFunc {
|
func Log() echo.MiddlewareFunc {
|
||||||
return func(h echo.HandlerFunc) echo.HandlerFunc {
|
return func(h echo.Handler) echo.Handler {
|
||||||
return func(c echo.Context) error {
|
return echo.HandlerFunc(func(c echo.Context) error {
|
||||||
req := c.Request()
|
req := c.Request()
|
||||||
res := c.Response()
|
res := c.Response()
|
||||||
logger := c.Logger()
|
logger := c.Logger()
|
||||||
@ -49,6 +49,6 @@ func Log() echo.MiddlewareFunc {
|
|||||||
|
|
||||||
logger.Infof("%s %s %s %s %s %d", remoteAddr, method, path, code, stop.Sub(start), size)
|
logger.Infof("%s %s %s %s %s %d", remoteAddr, method, path, code, stop.Sub(start), size)
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,38 +18,37 @@ func TestLog(t *testing.T) {
|
|||||||
req := test.NewRequest(echo.GET, "/", nil)
|
req := test.NewRequest(echo.GET, "/", nil)
|
||||||
rec := test.NewResponseRecorder()
|
rec := test.NewResponseRecorder()
|
||||||
c := echo.NewContext(req, rec, e)
|
c := echo.NewContext(req, rec, e)
|
||||||
h := func(c echo.Context) error {
|
h := Log()(echo.HandlerFunc(func(c echo.Context) error {
|
||||||
return c.String(http.StatusOK, "test")
|
return c.String(http.StatusOK, "test")
|
||||||
}
|
}))
|
||||||
mw := Log()(h)
|
|
||||||
|
|
||||||
// Status 2xx
|
// Status 2xx
|
||||||
mw(c)
|
h.Handle(c)
|
||||||
|
|
||||||
// Status 3xx
|
// Status 3xx
|
||||||
rec = test.NewResponseRecorder()
|
rec = test.NewResponseRecorder()
|
||||||
c = echo.NewContext(req, rec, e)
|
c = echo.NewContext(req, rec, e)
|
||||||
h = func(c echo.Context) error {
|
h = Log()(echo.HandlerFunc(func(c echo.Context) error {
|
||||||
return c.String(http.StatusTemporaryRedirect, "test")
|
return c.String(http.StatusTemporaryRedirect, "test")
|
||||||
}
|
}))
|
||||||
mw(c)
|
h.Handle(c)
|
||||||
|
|
||||||
// Status 4xx
|
// Status 4xx
|
||||||
rec = test.NewResponseRecorder()
|
rec = test.NewResponseRecorder()
|
||||||
c = echo.NewContext(req, rec, e)
|
c = echo.NewContext(req, rec, e)
|
||||||
h = func(c echo.Context) error {
|
h = Log()(echo.HandlerFunc(func(c echo.Context) error {
|
||||||
return c.String(http.StatusNotFound, "test")
|
return c.String(http.StatusNotFound, "test")
|
||||||
}
|
}))
|
||||||
mw(c)
|
h.Handle(c)
|
||||||
|
|
||||||
// Status 5xx with empty path
|
// Status 5xx with empty path
|
||||||
req = test.NewRequest(echo.GET, "", nil)
|
req = test.NewRequest(echo.GET, "", nil)
|
||||||
rec = test.NewResponseRecorder()
|
rec = test.NewResponseRecorder()
|
||||||
c = echo.NewContext(req, rec, e)
|
c = echo.NewContext(req, rec, e)
|
||||||
h = func(c echo.Context) error {
|
h = Log()(echo.HandlerFunc(func(c echo.Context) error {
|
||||||
return errors.New("error")
|
return errors.New("error")
|
||||||
}
|
}))
|
||||||
mw(c)
|
h.Handle(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLogIPAddress(t *testing.T) {
|
func TestLogIPAddress(t *testing.T) {
|
||||||
@ -60,25 +59,24 @@ func TestLogIPAddress(t *testing.T) {
|
|||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
e.Logger().(*log.Logger).SetOutput(buf)
|
e.Logger().(*log.Logger).SetOutput(buf)
|
||||||
ip := "127.0.0.1"
|
ip := "127.0.0.1"
|
||||||
h := func(c echo.Context) error {
|
h := Log()(echo.HandlerFunc(func(c echo.Context) error {
|
||||||
return c.String(http.StatusOK, "test")
|
return c.String(http.StatusOK, "test")
|
||||||
}
|
}))
|
||||||
mw := Log()(h)
|
|
||||||
|
|
||||||
// With X-Real-IP
|
// With X-Real-IP
|
||||||
req.Header().Add(echo.XRealIP, ip)
|
req.Header().Add(echo.XRealIP, ip)
|
||||||
mw(c)
|
h.Handle(c)
|
||||||
assert.Contains(t, buf.String(), ip)
|
assert.Contains(t, buf.String(), ip)
|
||||||
|
|
||||||
// With X-Forwarded-For
|
// With X-Forwarded-For
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
req.Header().Del(echo.XRealIP)
|
req.Header().Del(echo.XRealIP)
|
||||||
req.Header().Add(echo.XForwardedFor, ip)
|
req.Header().Add(echo.XForwardedFor, ip)
|
||||||
mw(c)
|
h.Handle(c)
|
||||||
assert.Contains(t, buf.String(), ip)
|
assert.Contains(t, buf.String(), ip)
|
||||||
|
|
||||||
// with req.RemoteAddr
|
// with req.RemoteAddr
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
mw(c)
|
h.Handle(c)
|
||||||
assert.Contains(t, buf.String(), ip)
|
assert.Contains(t, buf.String(), ip)
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ import (
|
|||||||
// Recover returns a middleware which recovers from panics anywhere in the chain
|
// Recover returns a middleware which recovers from panics anywhere in the chain
|
||||||
// and handles the control to the centralized HTTPErrorHandler.
|
// and handles the control to the centralized HTTPErrorHandler.
|
||||||
func Recover() echo.MiddlewareFunc {
|
func Recover() echo.MiddlewareFunc {
|
||||||
return func(h echo.HandlerFunc) echo.HandlerFunc {
|
return func(h echo.Handler) echo.Handler {
|
||||||
// TODO: Provide better stack trace `https://github.com/go-errors/errors` `https://github.com/docker/libcontainer/tree/master/stacktrace`
|
// TODO: Provide better stack trace `https://github.com/go-errors/errors` `https://github.com/docker/libcontainer/tree/master/stacktrace`
|
||||||
return func(c echo.Context) error {
|
return echo.HandlerFunc(func(c echo.Context) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
trace := make([]byte, 1<<16)
|
trace := make([]byte, 1<<16)
|
||||||
@ -23,6 +23,6 @@ func Recover() echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return h.Handle(c)
|
return h.Handle(c)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@ func TestRecover(t *testing.T) {
|
|||||||
req := test.NewRequest(echo.GET, "/", nil)
|
req := test.NewRequest(echo.GET, "/", nil)
|
||||||
rec := test.NewResponseRecorder()
|
rec := test.NewResponseRecorder()
|
||||||
c := echo.NewContext(req, rec, e)
|
c := echo.NewContext(req, rec, e)
|
||||||
h := func(c echo.Context) error {
|
h := Recover()(echo.HandlerFunc(func(c echo.Context) error {
|
||||||
panic("test")
|
panic("test")
|
||||||
}
|
}))
|
||||||
Recover()(h)(c)
|
h.Handle(c)
|
||||||
assert.Equal(t, http.StatusInternalServerError, rec.Status())
|
assert.Equal(t, http.StatusInternalServerError, rec.Status())
|
||||||
assert.Contains(t, rec.Body.String(), "panic recover")
|
assert.Contains(t, rec.Body.String(), "panic recover")
|
||||||
}
|
}
|
||||||
|
86
router.go
86
router.go
@ -15,20 +15,19 @@ type (
|
|||||||
ppath string
|
ppath string
|
||||||
pnames []string
|
pnames []string
|
||||||
methodHandler *methodHandler
|
methodHandler *methodHandler
|
||||||
echo *Echo
|
|
||||||
}
|
}
|
||||||
kind uint8
|
kind uint8
|
||||||
children []*node
|
children []*node
|
||||||
methodHandler struct {
|
methodHandler struct {
|
||||||
connect HandlerFunc
|
connect Handler
|
||||||
delete HandlerFunc
|
delete Handler
|
||||||
get HandlerFunc
|
get Handler
|
||||||
head HandlerFunc
|
head Handler
|
||||||
options HandlerFunc
|
options Handler
|
||||||
patch HandlerFunc
|
patch Handler
|
||||||
post HandlerFunc
|
post Handler
|
||||||
put HandlerFunc
|
put Handler
|
||||||
trace HandlerFunc
|
trace Handler
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,7 +47,20 @@ func NewRouter(e *Echo) *Router {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) Add(method, path string, h HandlerFunc, e *Echo) {
|
func (r *Router) Handle(h Handler) Handler {
|
||||||
|
return HandlerFunc(func(c Context) error {
|
||||||
|
method := c.Request().Method()
|
||||||
|
path := c.Request().URL().Path()
|
||||||
|
r.Find(method, path, c)
|
||||||
|
return h.Handle(c)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) Priority() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) Add(method, path string, h Handler, e *Echo) {
|
||||||
ppath := path // Pristine path
|
ppath := path // Pristine path
|
||||||
pnames := []string{} // Param names
|
pnames := []string{} // Param names
|
||||||
|
|
||||||
@ -80,7 +92,7 @@ func (r *Router) Add(method, path string, h HandlerFunc, e *Echo) {
|
|||||||
r.insert(method, path, h, skind, ppath, pnames, e)
|
r.insert(method, path, h, skind, ppath, pnames, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string, e *Echo) {
|
func (r *Router) insert(method, path string, h Handler, t kind, ppath string, pnames []string, e *Echo) {
|
||||||
// Adjust max param
|
// Adjust max param
|
||||||
l := len(pnames)
|
l := len(pnames)
|
||||||
if *e.maxParam < l {
|
if *e.maxParam < l {
|
||||||
@ -89,7 +101,7 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
|
|
||||||
cn := r.tree // Current node as root
|
cn := r.tree // Current node as root
|
||||||
if cn == nil {
|
if cn == nil {
|
||||||
panic("echo => invalid method")
|
panic("echo ⇛ invalid method")
|
||||||
}
|
}
|
||||||
search := path
|
search := path
|
||||||
|
|
||||||
@ -115,11 +127,10 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
cn.addHandler(method, h)
|
cn.addHandler(method, h)
|
||||||
cn.ppath = ppath
|
cn.ppath = ppath
|
||||||
cn.pnames = pnames
|
cn.pnames = pnames
|
||||||
cn.echo = e
|
|
||||||
}
|
}
|
||||||
} else if l < pl {
|
} else if l < pl {
|
||||||
// Split node
|
// Split node
|
||||||
n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames, cn.echo)
|
n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)
|
||||||
|
|
||||||
// Reset parent node
|
// Reset parent node
|
||||||
cn.kind = skind
|
cn.kind = skind
|
||||||
@ -129,7 +140,6 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
cn.methodHandler = new(methodHandler)
|
cn.methodHandler = new(methodHandler)
|
||||||
cn.ppath = ""
|
cn.ppath = ""
|
||||||
cn.pnames = nil
|
cn.pnames = nil
|
||||||
cn.echo = nil
|
|
||||||
|
|
||||||
cn.addChild(n)
|
cn.addChild(n)
|
||||||
|
|
||||||
@ -139,10 +149,9 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
cn.addHandler(method, h)
|
cn.addHandler(method, h)
|
||||||
cn.ppath = ppath
|
cn.ppath = ppath
|
||||||
cn.pnames = pnames
|
cn.pnames = pnames
|
||||||
cn.echo = e
|
|
||||||
} else {
|
} else {
|
||||||
// Create child node
|
// Create child node
|
||||||
n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames, e)
|
n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
|
||||||
n.addHandler(method, h)
|
n.addHandler(method, h)
|
||||||
cn.addChild(n)
|
cn.addChild(n)
|
||||||
}
|
}
|
||||||
@ -155,7 +164,7 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Create child node
|
// Create child node
|
||||||
n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames, e)
|
n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
|
||||||
n.addHandler(method, h)
|
n.addHandler(method, h)
|
||||||
cn.addChild(n)
|
cn.addChild(n)
|
||||||
} else {
|
} else {
|
||||||
@ -164,14 +173,13 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
cn.addHandler(method, h)
|
cn.addHandler(method, h)
|
||||||
cn.ppath = path
|
cn.ppath = path
|
||||||
cn.pnames = pnames
|
cn.pnames = pnames
|
||||||
cn.echo = e
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string, e *Echo) *node {
|
func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
|
||||||
return &node{
|
return &node{
|
||||||
kind: t,
|
kind: t,
|
||||||
label: pre[0],
|
label: pre[0],
|
||||||
@ -181,7 +189,6 @@ func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath s
|
|||||||
ppath: ppath,
|
ppath: ppath,
|
||||||
pnames: pnames,
|
pnames: pnames,
|
||||||
methodHandler: mh,
|
methodHandler: mh,
|
||||||
echo: e,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +223,7 @@ func (n *node) findChildByKind(t kind) *node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) addHandler(method string, h HandlerFunc) {
|
func (n *node) addHandler(method string, h Handler) {
|
||||||
switch method {
|
switch method {
|
||||||
case GET:
|
case GET:
|
||||||
n.methodHandler.get = h
|
n.methodHandler.get = h
|
||||||
@ -239,7 +246,7 @@ func (n *node) addHandler(method string, h HandlerFunc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) findHandler(method string) HandlerFunc {
|
func (n *node) findHandler(method string) Handler {
|
||||||
switch method {
|
switch method {
|
||||||
case GET:
|
case GET:
|
||||||
return n.methodHandler.get
|
return n.methodHandler.get
|
||||||
@ -273,10 +280,10 @@ func (n *node) check405() HandlerFunc {
|
|||||||
return notFoundHandler
|
return notFoundHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) Find(method, path string, context Context) (h HandlerFunc, e *Echo) {
|
func (r *Router) Find(method, path string, context Context) {
|
||||||
x := context.Object()
|
ctx := context.Object()
|
||||||
h = notFoundHandler
|
// h = notFoundHandler
|
||||||
e = r.echo
|
// e = r.echo
|
||||||
cn := r.tree // Current node as root
|
cn := r.tree // Current node as root
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -357,7 +364,7 @@ func (r *Router) Find(method, path string, context Context) (h HandlerFunc, e *E
|
|||||||
i, l := 0, len(search)
|
i, l := 0, len(search)
|
||||||
for ; i < l && search[i] != '/'; i++ {
|
for ; i < l && search[i] != '/'; i++ {
|
||||||
}
|
}
|
||||||
x.pvalues[n] = search[:i]
|
ctx.pvalues[n] = search[:i]
|
||||||
n++
|
n++
|
||||||
search = search[i:]
|
search = search[i:]
|
||||||
continue
|
continue
|
||||||
@ -370,30 +377,27 @@ func (r *Router) Find(method, path string, context Context) (h HandlerFunc, e *E
|
|||||||
// Not found
|
// Not found
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
x.pvalues[len(cn.pnames)-1] = search
|
ctx.pvalues[len(cn.pnames)-1] = search
|
||||||
goto End
|
goto End
|
||||||
}
|
}
|
||||||
|
|
||||||
End:
|
End:
|
||||||
x.path = cn.ppath
|
ctx.path = cn.ppath
|
||||||
x.pnames = cn.pnames
|
ctx.pnames = cn.pnames
|
||||||
h = cn.findHandler(method)
|
ctx.handler = cn.findHandler(method)
|
||||||
if cn.echo != nil {
|
|
||||||
e = cn.echo
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Slow zone...
|
// NOTE: Slow zone...
|
||||||
if h == nil {
|
if ctx.handler == nil {
|
||||||
h = cn.check405()
|
ctx.handler = cn.check405()
|
||||||
|
|
||||||
// Dig further for match-any, might have an empty value for *, e.g.
|
// Dig further for match-any, might have an empty value for *, e.g.
|
||||||
// serving a directory. Issue #207.
|
// serving a directory. Issue #207.
|
||||||
if cn = cn.findChildByKind(mkind); cn == nil {
|
if cn = cn.findChildByKind(mkind); cn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
x.pvalues[len(cn.pnames)-1] = ""
|
ctx.pvalues[len(cn.pnames)-1] = ""
|
||||||
if h = cn.findHandler(method); h == nil {
|
if ctx.handler = cn.findHandler(method); ctx.handler == nil {
|
||||||
h = cn.check405()
|
ctx.handler = cn.check405()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
199
router_test.go
199
router_test.go
@ -277,44 +277,38 @@ func TestRouterStatic(t *testing.T) {
|
|||||||
e := New()
|
e := New()
|
||||||
r := e.router
|
r := e.router
|
||||||
path := "/folders/a/files/echo.gif"
|
path := "/folders/a/files/echo.gif"
|
||||||
r.Add(GET, path, func(c Context) error {
|
r.Add(GET, path, HandlerFunc(func(c Context) error {
|
||||||
c.Set("path", path)
|
c.Set("path", path)
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
h, _ := r.Find(GET, path, c)
|
r.Find(GET, path, c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, path, c.Get("path"))
|
assert.Equal(t, path, c.Get("path"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterParam(t *testing.T) {
|
func TestRouterParam(t *testing.T) {
|
||||||
e := New()
|
e := New()
|
||||||
r := e.router
|
r := e.router
|
||||||
r.Add(GET, "/users/:id", func(c Context) error {
|
r.Add(GET, "/users/:id", HandlerFunc(func(c Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
h, _ := r.Find(GET, "/users/1", c)
|
r.Find(GET, "/users/1", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "1", c.P(0))
|
assert.Equal(t, "1", c.P(0))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterTwoParam(t *testing.T) {
|
func TestRouterTwoParam(t *testing.T) {
|
||||||
e := New()
|
e := New()
|
||||||
r := e.router
|
r := e.router
|
||||||
r.Add(GET, "/users/:uid/files/:fid", func(Context) error {
|
r.Add(GET, "/users/:uid/files/:fid", HandlerFunc(func(Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
|
|
||||||
h, _ := r.Find(GET, "/users/1/files/1", c)
|
r.Find(GET, "/users/1/files/1", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "1", c.P(0))
|
assert.Equal(t, "1", c.P(0))
|
||||||
assert.Equal(t, "1", c.P(1))
|
assert.Equal(t, "1", c.P(1))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterMatchAny(t *testing.T) {
|
func TestRouterMatchAny(t *testing.T) {
|
||||||
@ -322,46 +316,38 @@ func TestRouterMatchAny(t *testing.T) {
|
|||||||
r := e.router
|
r := e.router
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
r.Add(GET, "/", func(Context) error {
|
r.Add(GET, "/", HandlerFunc(func(Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/*", func(Context) error {
|
r.Add(GET, "/*", HandlerFunc(func(Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/*", func(Context) error {
|
r.Add(GET, "/users/*", HandlerFunc(func(Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
|
|
||||||
h, _ := r.Find(GET, "/", c)
|
r.Find(GET, "/", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "", c.P(0))
|
assert.Equal(t, "", c.P(0))
|
||||||
}
|
|
||||||
|
|
||||||
h, _ = r.Find(GET, "/download", c)
|
r.Find(GET, "/download", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "download", c.P(0))
|
assert.Equal(t, "download", c.P(0))
|
||||||
}
|
|
||||||
|
|
||||||
h, _ = r.Find(GET, "/users/joe", c)
|
r.Find(GET, "/users/joe", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "joe", c.P(0))
|
assert.Equal(t, "joe", c.P(0))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterMicroParam(t *testing.T) {
|
func TestRouterMicroParam(t *testing.T) {
|
||||||
e := New()
|
e := New()
|
||||||
r := e.router
|
r := e.router
|
||||||
r.Add(GET, "/:a/:b/:c", func(c Context) error {
|
r.Add(GET, "/:a/:b/:c", HandlerFunc(func(c Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
h, _ := r.Find(GET, "/1/2/3", c)
|
r.Find(GET, "/1/2/3", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "1", c.P(0))
|
assert.Equal(t, "1", c.P(0))
|
||||||
assert.Equal(t, "2", c.P(1))
|
assert.Equal(t, "2", c.P(1))
|
||||||
assert.Equal(t, "3", c.P(2))
|
assert.Equal(t, "3", c.P(2))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterMixParamMatchAny(t *testing.T) {
|
func TestRouterMixParamMatchAny(t *testing.T) {
|
||||||
@ -369,16 +355,14 @@ func TestRouterMixParamMatchAny(t *testing.T) {
|
|||||||
r := e.router
|
r := e.router
|
||||||
|
|
||||||
// Route
|
// Route
|
||||||
r.Add(GET, "/users/:id/*", func(c Context) error {
|
r.Add(GET, "/users/:id/*", HandlerFunc(func(c Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
|
|
||||||
h, _ := r.Find(GET, "/users/joe/comments", c)
|
r.Find(GET, "/users/joe/comments", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, "joe", c.P(0))
|
assert.Equal(t, "joe", c.P(0))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterMultiRoute(t *testing.T) {
|
func TestRouterMultiRoute(t *testing.T) {
|
||||||
@ -386,32 +370,29 @@ func TestRouterMultiRoute(t *testing.T) {
|
|||||||
r := e.router
|
r := e.router
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
r.Add(GET, "/users", func(c Context) error {
|
r.Add(GET, "/users", HandlerFunc(func(c Context) error {
|
||||||
c.Set("path", "/users")
|
c.Set("path", "/users")
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/:id", func(c Context) error {
|
r.Add(GET, "/users/:id", HandlerFunc(func(c Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
|
|
||||||
// Route > /users
|
// Route > /users
|
||||||
h, _ := r.Find(GET, "/users", c)
|
r.Find(GET, "/users", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, "/users", c.Get("path"))
|
assert.Equal(t, "/users", c.Get("path"))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/:id
|
// Route > /users/:id
|
||||||
h, _ = r.Find(GET, "/users/1", c)
|
r.Find(GET, "/users/1", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "1", c.P(0))
|
assert.Equal(t, "1", c.P(0))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /user
|
// Route > /user
|
||||||
h, _ = r.Find(GET, "/user", c)
|
c = NewContext(nil, nil, e)
|
||||||
if assert.IsType(t, new(HTTPError), h(c)) {
|
r.Find(GET, "/user", c)
|
||||||
he := h(c).(*HTTPError)
|
if assert.IsType(t, new(HTTPError), c.Handle(c)) {
|
||||||
|
he := c.Handle(c).(*HTTPError)
|
||||||
assert.Equal(t, http.StatusNotFound, he.code)
|
assert.Equal(t, http.StatusNotFound, he.code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,85 +402,71 @@ func TestRouterPriority(t *testing.T) {
|
|||||||
r := e.router
|
r := e.router
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
r.Add(GET, "/users", func(c Context) error {
|
r.Add(GET, "/users", HandlerFunc(func(c Context) error {
|
||||||
c.Set("a", 1)
|
c.Set("a", 1)
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/new", func(c Context) error {
|
r.Add(GET, "/users/new", HandlerFunc(func(c Context) error {
|
||||||
c.Set("b", 2)
|
c.Set("b", 2)
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/:id", func(c Context) error {
|
r.Add(GET, "/users/:id", HandlerFunc(func(c Context) error {
|
||||||
c.Set("c", 3)
|
c.Set("c", 3)
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/dew", func(c Context) error {
|
r.Add(GET, "/users/dew", HandlerFunc(func(c Context) error {
|
||||||
c.Set("d", 4)
|
c.Set("d", 4)
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/:id/files", func(c Context) error {
|
r.Add(GET, "/users/:id/files", HandlerFunc(func(c Context) error {
|
||||||
c.Set("e", 5)
|
c.Set("e", 5)
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/newsee", func(c Context) error {
|
r.Add(GET, "/users/newsee", HandlerFunc(func(c Context) error {
|
||||||
c.Set("f", 6)
|
c.Set("f", 6)
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/*", func(c Context) error {
|
r.Add(GET, "/users/*", HandlerFunc(func(c Context) error {
|
||||||
c.Set("g", 7)
|
c.Set("g", 7)
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
|
|
||||||
// Route > /users
|
// Route > /users
|
||||||
h, _ := r.Find(GET, "/users", c)
|
r.Find(GET, "/users", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, 1, c.Get("a"))
|
assert.Equal(t, 1, c.Get("a"))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/new
|
// Route > /users/new
|
||||||
h, _ = r.Find(GET, "/users/new", c)
|
r.Find(GET, "/users/new", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, 2, c.Get("b"))
|
assert.Equal(t, 2, c.Get("b"))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/:id
|
// Route > /users/:id
|
||||||
h, _ = r.Find(GET, "/users/1", c)
|
r.Find(GET, "/users/1", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, 3, c.Get("c"))
|
assert.Equal(t, 3, c.Get("c"))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/dew
|
// Route > /users/dew
|
||||||
h, _ = r.Find(GET, "/users/dew", c)
|
r.Find(GET, "/users/dew", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, 4, c.Get("d"))
|
assert.Equal(t, 4, c.Get("d"))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/:id/files
|
// Route > /users/:id/files
|
||||||
h, _ = r.Find(GET, "/users/1/files", c)
|
r.Find(GET, "/users/1/files", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, 5, c.Get("e"))
|
assert.Equal(t, 5, c.Get("e"))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/:id
|
// Route > /users/:id
|
||||||
h, _ = r.Find(GET, "/users/news", c)
|
r.Find(GET, "/users/news", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, 3, c.Get("c"))
|
assert.Equal(t, 3, c.Get("c"))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/*
|
// Route > /users/*
|
||||||
h, _ = r.Find(GET, "/users/joe/books", c)
|
r.Find(GET, "/users/joe/books", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, 7, c.Get("g"))
|
assert.Equal(t, 7, c.Get("g"))
|
||||||
assert.Equal(t, "joe/books", c.Param("_*"))
|
assert.Equal(t, "joe/books", c.Param("_*"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterParamNames(t *testing.T) {
|
func TestRouterParamNames(t *testing.T) {
|
||||||
@ -507,40 +474,34 @@ func TestRouterParamNames(t *testing.T) {
|
|||||||
r := e.router
|
r := e.router
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
r.Add(GET, "/users", func(c Context) error {
|
r.Add(GET, "/users", HandlerFunc(func(c Context) error {
|
||||||
c.Set("path", "/users")
|
c.Set("path", "/users")
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/:id", func(c Context) error {
|
r.Add(GET, "/users/:id", HandlerFunc(func(c Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
r.Add(GET, "/users/:uid/files/:fid", func(c Context) error {
|
r.Add(GET, "/users/:uid/files/:fid", HandlerFunc(func(c Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
|
|
||||||
// Route > /users
|
// Route > /users
|
||||||
h, _ := r.Find(GET, "/users", c)
|
r.Find(GET, "/users", c)
|
||||||
if assert.NotNil(t, h) {
|
c.Handle(c)
|
||||||
h(c)
|
|
||||||
assert.Equal(t, "/users", c.Get("path"))
|
assert.Equal(t, "/users", c.Get("path"))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/:id
|
// Route > /users/:id
|
||||||
h, _ = r.Find(GET, "/users/1", c)
|
r.Find(GET, "/users/1", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "id", c.Object().pnames[0])
|
assert.Equal(t, "id", c.Object().pnames[0])
|
||||||
assert.Equal(t, "1", c.P(0))
|
assert.Equal(t, "1", c.P(0))
|
||||||
}
|
|
||||||
|
|
||||||
// Route > /users/:uid/files/:fid
|
// Route > /users/:uid/files/:fid
|
||||||
h, _ = r.Find(GET, "/users/1/files/1", c)
|
r.Find(GET, "/users/1/files/1", c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
assert.Equal(t, "uid", c.Object().pnames[0])
|
assert.Equal(t, "uid", c.Object().pnames[0])
|
||||||
assert.Equal(t, "1", c.P(0))
|
assert.Equal(t, "1", c.P(0))
|
||||||
assert.Equal(t, "fid", c.Object().pnames[1])
|
assert.Equal(t, "fid", c.Object().pnames[1])
|
||||||
assert.Equal(t, "1", c.P(1))
|
assert.Equal(t, "1", c.P(1))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterAPI(t *testing.T) {
|
func TestRouterAPI(t *testing.T) {
|
||||||
@ -548,21 +509,19 @@ func TestRouterAPI(t *testing.T) {
|
|||||||
r := e.router
|
r := e.router
|
||||||
|
|
||||||
for _, route := range api {
|
for _, route := range api {
|
||||||
r.Add(route.Method, route.Path, func(c Context) error {
|
r.Add(route.Method, route.Path, HandlerFunc(func(c Context) error {
|
||||||
return nil
|
return nil
|
||||||
}, e)
|
}), e)
|
||||||
}
|
}
|
||||||
c := NewContext(nil, nil, e)
|
c := NewContext(nil, nil, e)
|
||||||
for _, route := range api {
|
for _, route := range api {
|
||||||
h, _ := r.Find(route.Method, route.Path, c)
|
r.Find(route.Method, route.Path, c)
|
||||||
if assert.NotNil(t, h) {
|
|
||||||
for i, n := range c.Object().pnames {
|
for i, n := range c.Object().pnames {
|
||||||
if assert.NotEmpty(t, n) {
|
if assert.NotEmpty(t, n) {
|
||||||
assert.Equal(t, ":"+n, c.P(i))
|
assert.Equal(t, ":"+n, c.P(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h(c)
|
c.Handle(c)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user