mirror of
https://github.com/labstack/echo.git
synced 2025-04-19 12:12:51 +02:00
Merge pull request #191 from labstack/issue-190
StripTrailingSlash is now an option
This commit is contained in:
commit
e7b1358de5
13
echo.go
13
echo.go
@ -34,6 +34,7 @@ type (
|
|||||||
renderer Renderer
|
renderer Renderer
|
||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
debug bool
|
debug bool
|
||||||
|
stripTrailingSlash bool
|
||||||
router *Router
|
router *Router
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,14 +249,14 @@ func (e *Echo) SetRenderer(r Renderer) {
|
|||||||
e.renderer = r
|
e.renderer = r
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDebug sets debug mode.
|
// Debug enables debug mode.
|
||||||
func (e *Echo) SetDebug(on bool) {
|
func (e *Echo) Debug() {
|
||||||
e.debug = on
|
e.debug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug returns debug mode.
|
// StripTrailingSlash enables removing trailing slash from the request path.
|
||||||
func (e *Echo) Debug() bool {
|
func (e *Echo) StripTrailingSlash() {
|
||||||
return e.debug
|
e.stripTrailingSlash = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use adds handler to the middleware chain.
|
// Use adds handler to the middleware chain.
|
||||||
|
13
echo_test.go
13
echo_test.go
@ -33,8 +33,8 @@ func TestEcho(t *testing.T) {
|
|||||||
assert.NotNil(t, e.Router())
|
assert.NotNil(t, e.Router())
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
e.SetDebug(true)
|
e.Debug()
|
||||||
assert.True(t, e.Debug())
|
assert.True(t, e.debug)
|
||||||
|
|
||||||
// DefaultHTTPErrorHandler
|
// DefaultHTTPErrorHandler
|
||||||
e.DefaultHTTPErrorHandler(errors.New("error"), c)
|
e.DefaultHTTPErrorHandler(errors.New("error"), c)
|
||||||
@ -403,6 +403,15 @@ func TestEchoServer(t *testing.T) {
|
|||||||
assert.IsType(t, &http.Server{}, s)
|
assert.IsType(t, &http.Server{}, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStripTrailingSlash(t *testing.T) {
|
||||||
|
e := New()
|
||||||
|
e.StripTrailingSlash()
|
||||||
|
r, _ := http.NewRequest(GET, "/users/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
e.ServeHTTP(w, r)
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -17,7 +17,7 @@ func main() {
|
|||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|
||||||
// Debug mode
|
// Debug mode
|
||||||
e.SetDebug(true)
|
e.Debug()
|
||||||
|
|
||||||
//------------
|
//------------
|
||||||
// Middleware
|
// Middleware
|
||||||
@ -37,16 +37,6 @@ func main() {
|
|||||||
return false
|
return false
|
||||||
}))
|
}))
|
||||||
|
|
||||||
//-------
|
|
||||||
// Slash
|
|
||||||
//-------
|
|
||||||
|
|
||||||
e.Use(mw.StripTrailingSlash())
|
|
||||||
|
|
||||||
// or
|
|
||||||
|
|
||||||
// e.Use(mw.RedirectToSlash())
|
|
||||||
|
|
||||||
// Gzip
|
// Gzip
|
||||||
e.Use(mw.Gzip())
|
e.Use(mw.Gzip())
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
func TestRecover(t *testing.T) {
|
func TestRecover(t *testing.T) {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
e.SetDebug(true)
|
e.Debug()
|
||||||
req, _ := http.NewRequest(echo.GET, "/", nil)
|
req, _ := http.NewRequest(echo.GET, "/", nil)
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
c := echo.NewContext(req, echo.NewResponse(rec), e)
|
c := echo.NewContext(req, echo.NewResponse(rec), e)
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
package middleware
|
|
||||||
|
|
||||||
import "github.com/labstack/echo"
|
|
||||||
|
|
||||||
// StripTrailingSlash returns a middleware which removes trailing slash from request
|
|
||||||
// path.
|
|
||||||
func StripTrailingSlash() echo.HandlerFunc {
|
|
||||||
return func(c *echo.Context) error {
|
|
||||||
p := c.Request().URL.Path
|
|
||||||
l := len(p)
|
|
||||||
if p[l-1] == '/' {
|
|
||||||
c.Request().URL.Path = p[:l-1]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/labstack/echo"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStripTrailingSlash(t *testing.T) {
|
|
||||||
req, _ := http.NewRequest(echo.GET, "/users/", nil)
|
|
||||||
rec := httptest.NewRecorder()
|
|
||||||
c := echo.NewContext(req, echo.NewResponse(rec), echo.New())
|
|
||||||
StripTrailingSlash()(c)
|
|
||||||
assert.Equal(t, "/users", c.Request().URL.Path)
|
|
||||||
}
|
|
54
router.go
54
router.go
@ -75,7 +75,7 @@ func (r *Router) Add(method, path string, h HandlerFunc, e *Echo) {
|
|||||||
} else if path[i] == '*' {
|
} else if path[i] == '*' {
|
||||||
r.insert(method, path[:i], nil, stype, nil, e)
|
r.insert(method, path[:i], nil, stype, nil, e)
|
||||||
pnames = append(pnames, "_name")
|
pnames = append(pnames, "_name")
|
||||||
r.insert(method, path[:i+1], h, mtype, pnames, e)
|
r.insert(method, path[:i + 1], h, mtype, pnames, e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,59 +215,59 @@ func (n *node) findChildWithType(t ntype) *node {
|
|||||||
func (r *Router) findTree(method string) (n *node) {
|
func (r *Router) findTree(method string) (n *node) {
|
||||||
switch method[0] {
|
switch method[0] {
|
||||||
case 'G': // GET
|
case 'G': // GET
|
||||||
m := uint32(method[2])<<8 | uint32(method[1])<<16 | uint32(method[0])<<24
|
m := uint32(method[2]) << 8 | uint32(method[1]) << 16 | uint32(method[0]) << 24
|
||||||
if m == 0x47455400 {
|
if m == 0x47455400 {
|
||||||
n = r.getTree
|
n = r.getTree
|
||||||
}
|
}
|
||||||
case 'P': // POST, PUT or PATCH
|
case 'P': // POST, PUT or PATCH
|
||||||
switch method[1] {
|
switch method[1] {
|
||||||
case 'O': // POST
|
case 'O': // POST
|
||||||
m := uint32(method[3]) | uint32(method[2])<<8 | uint32(method[1])<<16 |
|
m := uint32(method[3]) | uint32(method[2]) << 8 | uint32(method[1]) << 16 |
|
||||||
uint32(method[0])<<24
|
uint32(method[0]) << 24
|
||||||
if m == 0x504f5354 {
|
if m == 0x504f5354 {
|
||||||
n = r.postTree
|
n = r.postTree
|
||||||
}
|
}
|
||||||
case 'U': // PUT
|
case 'U': // PUT
|
||||||
m := uint32(method[2])<<8 | uint32(method[1])<<16 | uint32(method[0])<<24
|
m := uint32(method[2]) << 8 | uint32(method[1]) << 16 | uint32(method[0]) << 24
|
||||||
if m == 0x50555400 {
|
if m == 0x50555400 {
|
||||||
n = r.putTree
|
n = r.putTree
|
||||||
}
|
}
|
||||||
case 'A': // PATCH
|
case 'A': // PATCH
|
||||||
m := uint64(method[4])<<24 | uint64(method[3])<<32 | uint64(method[2])<<40 |
|
m := uint64(method[4]) << 24 | uint64(method[3]) << 32 | uint64(method[2]) << 40 |
|
||||||
uint64(method[1])<<48 | uint64(method[0])<<56
|
uint64(method[1]) << 48 | uint64(method[0]) << 56
|
||||||
if m == 0x5041544348000000 {
|
if m == 0x5041544348000000 {
|
||||||
n = r.patchTree
|
n = r.patchTree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'D': // DELETE
|
case 'D': // DELETE
|
||||||
m := uint64(method[5])<<16 | uint64(method[4])<<24 | uint64(method[3])<<32 |
|
m := uint64(method[5]) << 16 | uint64(method[4]) << 24 | uint64(method[3]) << 32 |
|
||||||
uint64(method[2])<<40 | uint64(method[1])<<48 | uint64(method[0])<<56
|
uint64(method[2]) << 40 | uint64(method[1]) << 48 | uint64(method[0]) << 56
|
||||||
if m == 0x44454c4554450000 {
|
if m == 0x44454c4554450000 {
|
||||||
n = r.deleteTree
|
n = r.deleteTree
|
||||||
}
|
}
|
||||||
case 'C': // CONNECT
|
case 'C': // CONNECT
|
||||||
m := uint64(method[6])<<8 | uint64(method[5])<<16 | uint64(method[4])<<24 |
|
m := uint64(method[6]) << 8 | uint64(method[5]) << 16 | uint64(method[4]) << 24 |
|
||||||
uint64(method[3])<<32 | uint64(method[2])<<40 | uint64(method[1])<<48 |
|
uint64(method[3]) << 32 | uint64(method[2]) << 40 | uint64(method[1]) << 48 |
|
||||||
uint64(method[0])<<56
|
uint64(method[0]) << 56
|
||||||
if m == 0x434f4e4e45435400 {
|
if m == 0x434f4e4e45435400 {
|
||||||
n = r.connectTree
|
n = r.connectTree
|
||||||
}
|
}
|
||||||
case 'H': // HEAD
|
case 'H': // HEAD
|
||||||
m := uint32(method[3]) | uint32(method[2])<<8 | uint32(method[1])<<16 |
|
m := uint32(method[3]) | uint32(method[2]) << 8 | uint32(method[1]) << 16 |
|
||||||
uint32(method[0])<<24
|
uint32(method[0]) << 24
|
||||||
if m == 0x48454144 {
|
if m == 0x48454144 {
|
||||||
n = r.headTree
|
n = r.headTree
|
||||||
}
|
}
|
||||||
case 'O': // OPTIONS
|
case 'O': // OPTIONS
|
||||||
m := uint64(method[6])<<8 | uint64(method[5])<<16 | uint64(method[4])<<24 |
|
m := uint64(method[6]) << 8 | uint64(method[5]) << 16 | uint64(method[4]) << 24 |
|
||||||
uint64(method[3])<<32 | uint64(method[2])<<40 | uint64(method[1])<<48 |
|
uint64(method[3]) << 32 | uint64(method[2]) << 40 | uint64(method[1]) << 48 |
|
||||||
uint64(method[0])<<56
|
uint64(method[0]) << 56
|
||||||
if m == 0x4f5054494f4e5300 {
|
if m == 0x4f5054494f4e5300 {
|
||||||
n = r.optionsTree
|
n = r.optionsTree
|
||||||
}
|
}
|
||||||
case 'T': // TRACE
|
case 'T': // TRACE
|
||||||
m := uint64(method[4])<<24 | uint64(method[3])<<32 | uint64(method[2])<<40 |
|
m := uint64(method[4]) << 24 | uint64(method[3]) << 32 | uint64(method[2]) << 40 |
|
||||||
uint64(method[1])<<48 | uint64(method[0])<<56
|
uint64(method[1]) << 48 | uint64(method[0]) << 56
|
||||||
if m == 0x5452414345000000 {
|
if m == 0x5452414345000000 {
|
||||||
n = r.traceTree
|
n = r.traceTree
|
||||||
}
|
}
|
||||||
@ -282,11 +282,19 @@ func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo
|
|||||||
h = badRequestHandler
|
h = badRequestHandler
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
search := path
|
|
||||||
|
// Strip trailing slash
|
||||||
|
if r.echo.stripTrailingSlash {
|
||||||
|
l := len(path)
|
||||||
|
if path[l - 1] == '/' {
|
||||||
|
path = path[:l - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
search = path
|
||||||
c *node // Child node
|
c *node // Child node
|
||||||
n int // Param counter
|
n int // Param counter
|
||||||
nt ntype // Next type
|
nt ntype // Next type
|
||||||
nn *node // Next node
|
nn *node // Next node
|
||||||
ns string // Next search
|
ns string // Next search
|
||||||
@ -361,7 +369,7 @@ func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Param node
|
// Param node
|
||||||
Param:
|
Param:
|
||||||
c = cn.findChildWithType(ptype)
|
c = cn.findChildWithType(ptype)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
// Save next
|
// Save next
|
||||||
@ -381,7 +389,7 @@ func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match-any node
|
// Match-any node
|
||||||
MatchAny:
|
MatchAny:
|
||||||
// c = cn.getChild()
|
// c = cn.getChild()
|
||||||
c = cn.findChildWithType(mtype)
|
c = cn.findChildWithType(mtype)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
|
@ -50,6 +50,12 @@ Enables debug mode.
|
|||||||
|
|
||||||
`Echo.DisableColoredLog()`
|
`Echo.DisableColoredLog()`
|
||||||
|
|
||||||
|
### StripTrailingSlash
|
||||||
|
|
||||||
|
StripTrailingSlash enables removing trailing slash from the request path.
|
||||||
|
|
||||||
|
`e.StripTrailingSlash()`
|
||||||
|
|
||||||
## Routing
|
## Routing
|
||||||
|
|
||||||
Echo's router is [fast, optimized](https://github.com/labstack/echo#benchmark) and
|
Echo's router is [fast, optimized](https://github.com/labstack/echo#benchmark) and
|
||||||
@ -210,16 +216,6 @@ to the centralized [HTTPErrorHandler](#error-handling).
|
|||||||
e.Use(mw.Recover())
|
e.Use(mw.Recover())
|
||||||
```
|
```
|
||||||
|
|
||||||
### StripTrailingSlash
|
|
||||||
|
|
||||||
StripTrailingSlash middleware removes the trailing slash from request path.
|
|
||||||
|
|
||||||
*Example*
|
|
||||||
|
|
||||||
```go
|
|
||||||
e.Use(mw.StripTrailingSlash())
|
|
||||||
```
|
|
||||||
|
|
||||||
[Examples](https://github.com/labstack/echo/tree/master/examples/middleware)
|
[Examples](https://github.com/labstack/echo/tree/master/examples/middleware)
|
||||||
|
|
||||||
## Request
|
## Request
|
||||||
|
Loading…
x
Reference in New Issue
Block a user