mirror of
				https://github.com/labstack/echo.git
				synced 2025-10-30 23:57:38 +02:00 
			
		
		
		
	StripTrailingSlash is now an option
Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
		
							
								
								
									
										13
									
								
								echo.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								echo.go
									
									
									
									
									
								
							| @@ -34,6 +34,7 @@ type ( | ||||
| 		renderer                Renderer | ||||
| 		pool                    sync.Pool | ||||
| 		debug                   bool | ||||
| 		stripTrailingSlash      bool | ||||
| 		router                  *Router | ||||
| 	} | ||||
|  | ||||
| @@ -248,14 +249,14 @@ func (e *Echo) SetRenderer(r Renderer) { | ||||
| 	e.renderer = r | ||||
| } | ||||
|  | ||||
| // SetDebug sets debug mode. | ||||
| func (e *Echo) SetDebug(on bool) { | ||||
| 	e.debug = on | ||||
| // Debug enables debug mode. | ||||
| func (e *Echo) Debug() { | ||||
| 	e.debug = true | ||||
| } | ||||
|  | ||||
| // Debug returns debug mode. | ||||
| func (e *Echo) Debug() bool { | ||||
| 	return e.debug | ||||
| // StripTrailingSlash enables removing trailing slash from the request path. | ||||
| func (e *Echo) StripTrailingSlash() { | ||||
| 	e.stripTrailingSlash = true | ||||
| } | ||||
|  | ||||
| // 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()) | ||||
|  | ||||
| 	// Debug | ||||
| 	e.SetDebug(true) | ||||
| 	assert.True(t, e.Debug()) | ||||
| 	e.Debug() | ||||
| 	assert.True(t, e.debug) | ||||
|  | ||||
| 	// DefaultHTTPErrorHandler | ||||
| 	e.DefaultHTTPErrorHandler(errors.New("error"), c) | ||||
| @@ -403,6 +403,15 @@ func TestEchoServer(t *testing.T) { | ||||
| 	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) { | ||||
| 	m := fmt.Sprintf("%c%s", method[0], strings.ToLower(method[1:])) | ||||
| 	p := reflect.ValueOf(path) | ||||
|   | ||||
| @@ -17,7 +17,7 @@ func main() { | ||||
| 	e := echo.New() | ||||
|  | ||||
| 	// Debug mode | ||||
| 	e.SetDebug(true) | ||||
| 	e.Debug() | ||||
|  | ||||
| 	//------------ | ||||
| 	// Middleware | ||||
| @@ -37,16 +37,6 @@ func main() { | ||||
| 		return false | ||||
| 	})) | ||||
|  | ||||
| 	//------- | ||||
| 	// Slash | ||||
| 	//------- | ||||
|  | ||||
| 	e.Use(mw.StripTrailingSlash()) | ||||
|  | ||||
| 	// or | ||||
|  | ||||
| 	//	e.Use(mw.RedirectToSlash()) | ||||
|  | ||||
| 	// Gzip | ||||
| 	e.Use(mw.Gzip()) | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import ( | ||||
|  | ||||
| func TestRecover(t *testing.T) { | ||||
| 	e := echo.New() | ||||
| 	e.SetDebug(true) | ||||
| 	e.Debug() | ||||
| 	req, _ := http.NewRequest(echo.GET, "/", nil) | ||||
| 	rec := httptest.NewRecorder() | ||||
| 	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] == '*' { | ||||
| 			r.insert(method, path[:i], nil, stype, nil, e) | ||||
| 			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 | ||||
| 		} | ||||
| 	} | ||||
| @@ -215,59 +215,59 @@ func (n *node) findChildWithType(t ntype) *node { | ||||
| func (r *Router) findTree(method string) (n *node) { | ||||
| 	switch method[0] { | ||||
| 	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 { | ||||
| 			n = r.getTree | ||||
| 		} | ||||
| 	case 'P': // POST, PUT or PATCH | ||||
| 		switch method[1] { | ||||
| 		case 'O': // POST | ||||
| 			m := uint32(method[3]) | uint32(method[2])<<8 | uint32(method[1])<<16 | | ||||
| 				uint32(method[0])<<24 | ||||
| 			m := uint32(method[3]) | uint32(method[2]) << 8 | uint32(method[1]) << 16 | | ||||
| 			uint32(method[0]) << 24 | ||||
| 			if m == 0x504f5354 { | ||||
| 				n = r.postTree | ||||
| 			} | ||||
| 		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 { | ||||
| 				n = r.putTree | ||||
| 			} | ||||
| 		case 'A': // PATCH | ||||
| 			m := uint64(method[4])<<24 | uint64(method[3])<<32 | uint64(method[2])<<40 | | ||||
| 				uint64(method[1])<<48 | uint64(method[0])<<56 | ||||
| 			m := uint64(method[4]) << 24 | uint64(method[3]) << 32 | uint64(method[2]) << 40 | | ||||
| 			uint64(method[1]) << 48 | uint64(method[0]) << 56 | ||||
| 			if m == 0x5041544348000000 { | ||||
| 				n = r.patchTree | ||||
| 			} | ||||
| 		} | ||||
| 	case 'D': // DELETE | ||||
| 		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 | ||||
| 		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 | ||||
| 		if m == 0x44454c4554450000 { | ||||
| 			n = r.deleteTree | ||||
| 		} | ||||
| 	case 'C': // CONNECT | ||||
| 		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[0])<<56 | ||||
| 		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[0]) << 56 | ||||
| 		if m == 0x434f4e4e45435400 { | ||||
| 			n = r.connectTree | ||||
| 		} | ||||
| 	case 'H': // HEAD | ||||
| 		m := uint32(method[3]) | uint32(method[2])<<8 | uint32(method[1])<<16 | | ||||
| 			uint32(method[0])<<24 | ||||
| 		m := uint32(method[3]) | uint32(method[2]) << 8 | uint32(method[1]) << 16 | | ||||
| 		uint32(method[0]) << 24 | ||||
| 		if m == 0x48454144 { | ||||
| 			n = r.headTree | ||||
| 		} | ||||
| 	case 'O': // OPTIONS | ||||
| 		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[0])<<56 | ||||
| 		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[0]) << 56 | ||||
| 		if m == 0x4f5054494f4e5300 { | ||||
| 			n = r.optionsTree | ||||
| 		} | ||||
| 	case 'T': // TRACE | ||||
| 		m := uint64(method[4])<<24 | uint64(method[3])<<32 | uint64(method[2])<<40 | | ||||
| 			uint64(method[1])<<48 | uint64(method[0])<<56 | ||||
| 		m := uint64(method[4]) << 24 | uint64(method[3]) << 32 | uint64(method[2]) << 40 | | ||||
| 		uint64(method[1]) << 48 | uint64(method[0]) << 56 | ||||
| 		if m == 0x5452414345000000 { | ||||
| 			n = r.traceTree | ||||
| 		} | ||||
| @@ -282,11 +282,19 @@ func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo | ||||
| 		h = badRequestHandler | ||||
| 		return | ||||
| 	} | ||||
| 	search := path | ||||
|  | ||||
| 	// Strip trailing slash | ||||
| 	if r.echo.stripTrailingSlash { | ||||
| 		l := len(path) | ||||
| 		if path[l - 1] == '/' { | ||||
| 			path = path[:l - 1] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var ( | ||||
| 		search = path | ||||
| 		c  *node  // Child node | ||||
| 		n  int    // Param counter | ||||
| 		n int    // Param counter | ||||
| 		nt ntype  // Next type | ||||
| 		nn *node  // Next node | ||||
| 		ns string // Next search | ||||
| @@ -361,7 +369,7 @@ func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo | ||||
| 		} | ||||
|  | ||||
| 		// Param node | ||||
| 	Param: | ||||
| 		Param: | ||||
| 		c = cn.findChildWithType(ptype) | ||||
| 		if c != nil { | ||||
| 			// Save next | ||||
| @@ -381,7 +389,7 @@ func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo | ||||
| 		} | ||||
|  | ||||
| 		// Match-any node | ||||
| 	MatchAny: | ||||
| 		MatchAny: | ||||
| 		//		c = cn.getChild() | ||||
| 		c = cn.findChildWithType(mtype) | ||||
| 		if c != nil { | ||||
|   | ||||
| @@ -50,6 +50,12 @@ Enables debug mode. | ||||
|  | ||||
| `Echo.DisableColoredLog()` | ||||
|  | ||||
| ### StripTrailingSlash | ||||
|  | ||||
| StripTrailingSlash enables removing trailing slash from the request path. | ||||
|  | ||||
| `e.StripTrailingSlash()` | ||||
|  | ||||
| ## Routing | ||||
|  | ||||
| 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()) | ||||
| ``` | ||||
|  | ||||
| ### 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) | ||||
|  | ||||
| ## Request | ||||
|   | ||||
		Reference in New Issue
	
	Block a user