mirror of
https://github.com/labstack/echo.git
synced 2025-04-23 12:18:53 +02:00
Allow arbitrary HTTP method types to be added as routes
This commit is contained in:
parent
a327884b68
commit
cba12a570e
5
echo.go
5
echo.go
@ -492,8 +492,11 @@ func (e *Echo) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) *R
|
|||||||
return e.Add(RouteNotFound, path, h, m...)
|
return e.Add(RouteNotFound, path, h, m...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any registers a new route for all HTTP methods and path with matching handler
|
// Any registers a new route for all HTTP methods (supported by Echo) and path with matching handler
|
||||||
// in the router with optional route-level middleware.
|
// in the router with optional route-level middleware.
|
||||||
|
//
|
||||||
|
// Note: this method only adds specific set of supported HTTP methods as handler and is not true
|
||||||
|
// "catch-any-arbitrary-method" way of matching requests.
|
||||||
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
|
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
|
||||||
routes := make([]*Route, len(methods))
|
routes := make([]*Route, len(methods))
|
||||||
for i, m := range methods {
|
for i, m := range methods {
|
||||||
|
19
router.go
19
router.go
@ -51,6 +51,7 @@ type (
|
|||||||
put *routeMethod
|
put *routeMethod
|
||||||
trace *routeMethod
|
trace *routeMethod
|
||||||
report *routeMethod
|
report *routeMethod
|
||||||
|
anyOther map[string]*routeMethod
|
||||||
allowHeader string
|
allowHeader string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -75,7 +76,8 @@ func (m *routeMethods) isHandler() bool {
|
|||||||
m.propfind != nil ||
|
m.propfind != nil ||
|
||||||
m.put != nil ||
|
m.put != nil ||
|
||||||
m.trace != nil ||
|
m.trace != nil ||
|
||||||
m.report != nil
|
m.report != nil ||
|
||||||
|
len(m.anyOther) != 0
|
||||||
// RouteNotFound/404 is not considered as a handler
|
// RouteNotFound/404 is not considered as a handler
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +123,10 @@ func (m *routeMethods) updateAllowHeader() {
|
|||||||
if m.report != nil {
|
if m.report != nil {
|
||||||
buf.WriteString(", REPORT")
|
buf.WriteString(", REPORT")
|
||||||
}
|
}
|
||||||
|
for method := range m.anyOther { // for simplicity, we use map and therefore order is not deterministic here
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(method)
|
||||||
|
}
|
||||||
m.allowHeader = buf.String()
|
m.allowHeader = buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,6 +414,15 @@ func (n *node) addMethod(method string, h *routeMethod) {
|
|||||||
case RouteNotFound:
|
case RouteNotFound:
|
||||||
n.notFoundHandler = h
|
n.notFoundHandler = h
|
||||||
return // RouteNotFound/404 is not considered as a handler so no further logic needs to be executed
|
return // RouteNotFound/404 is not considered as a handler so no further logic needs to be executed
|
||||||
|
default:
|
||||||
|
if n.methods.anyOther == nil {
|
||||||
|
n.methods.anyOther = make(map[string]*routeMethod)
|
||||||
|
}
|
||||||
|
if h.handler == nil {
|
||||||
|
delete(n.methods.anyOther, method)
|
||||||
|
} else {
|
||||||
|
n.methods.anyOther[method] = h
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n.methods.updateAllowHeader()
|
n.methods.updateAllowHeader()
|
||||||
@ -439,7 +454,7 @@ func (n *node) findMethod(method string) *routeMethod {
|
|||||||
case REPORT:
|
case REPORT:
|
||||||
return n.methods.report
|
return n.methods.report
|
||||||
default: // RouteNotFound/404 is not considered as a handler
|
default: // RouteNotFound/404 is not considered as a handler
|
||||||
return nil
|
return n.methods.anyOther[method]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,6 +716,67 @@ func TestRouterParam(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRouter_addAndMatchAllSupportedMethods(t *testing.T) {
|
||||||
|
var testCases = []struct {
|
||||||
|
name string
|
||||||
|
givenNoAddRoute bool
|
||||||
|
whenMethod string
|
||||||
|
expectPath string
|
||||||
|
expectError string
|
||||||
|
}{
|
||||||
|
{name: "ok, CONNECT", whenMethod: http.MethodConnect},
|
||||||
|
{name: "ok, DELETE", whenMethod: http.MethodDelete},
|
||||||
|
{name: "ok, GET", whenMethod: http.MethodGet},
|
||||||
|
{name: "ok, HEAD", whenMethod: http.MethodHead},
|
||||||
|
{name: "ok, OPTIONS", whenMethod: http.MethodOptions},
|
||||||
|
{name: "ok, PATCH", whenMethod: http.MethodPatch},
|
||||||
|
{name: "ok, POST", whenMethod: http.MethodPost},
|
||||||
|
{name: "ok, PROPFIND", whenMethod: PROPFIND},
|
||||||
|
{name: "ok, PUT", whenMethod: http.MethodPut},
|
||||||
|
{name: "ok, TRACE", whenMethod: http.MethodTrace},
|
||||||
|
{name: "ok, REPORT", whenMethod: REPORT},
|
||||||
|
{name: "ok, NON_TRADITIONAL_METHOD", whenMethod: "NON_TRADITIONAL_METHOD"},
|
||||||
|
{
|
||||||
|
name: "ok, NOT_EXISTING_METHOD",
|
||||||
|
whenMethod: "NOT_EXISTING_METHOD",
|
||||||
|
givenNoAddRoute: true,
|
||||||
|
expectPath: "/*",
|
||||||
|
expectError: "code=405, message=Method Not Allowed",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
e := New()
|
||||||
|
|
||||||
|
e.GET("/*", handlerFunc)
|
||||||
|
|
||||||
|
if !tc.givenNoAddRoute {
|
||||||
|
e.Add(tc.whenMethod, "/my/*", handlerFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := httptest.NewRequest(tc.whenMethod, "/my/some-url", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c := e.NewContext(req, rec).(*context)
|
||||||
|
|
||||||
|
e.router.Find(tc.whenMethod, "/my/some-url", c)
|
||||||
|
err := c.handler(c)
|
||||||
|
|
||||||
|
if tc.expectError != "" {
|
||||||
|
assert.EqualError(t, err, tc.expectError)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectPath := "/my/*"
|
||||||
|
if tc.expectPath != "" {
|
||||||
|
expectPath = tc.expectPath
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectPath, c.Path())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMethodNotAllowedAndNotFound(t *testing.T) {
|
func TestMethodNotAllowedAndNotFound(t *testing.T) {
|
||||||
e := New()
|
e := New()
|
||||||
r := e.router
|
r := e.router
|
||||||
@ -2634,6 +2695,25 @@ func TestRouterHandleMethodOptions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRouterAllowHeaderForAnyOtherMethodType(t *testing.T) {
|
||||||
|
e := New()
|
||||||
|
r := e.router
|
||||||
|
|
||||||
|
r.Add(http.MethodGet, "/users", handlerFunc)
|
||||||
|
r.Add("COPY", "/users", handlerFunc)
|
||||||
|
r.Add("LOCK", "/users", handlerFunc)
|
||||||
|
|
||||||
|
req := httptest.NewRequest("TEST", "/users", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c := e.NewContext(req, rec).(*context)
|
||||||
|
|
||||||
|
r.Find("TEST", "/users", c)
|
||||||
|
err := c.handler(c)
|
||||||
|
|
||||||
|
assert.EqualError(t, err, "code=405, message=Method Not Allowed")
|
||||||
|
assert.ElementsMatch(t, []string{"COPY", "GET", "LOCK", "OPTIONS"}, strings.Split(c.Response().Header().Get(HeaderAllow), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
func benchmarkRouterRoutes(b *testing.B, routes []*Route, routesToFind []*Route) {
|
func benchmarkRouterRoutes(b *testing.B, routes []*Route, routesToFind []*Route) {
|
||||||
e := New()
|
e := New()
|
||||||
r := e.router
|
r := e.router
|
||||||
|
Loading…
x
Reference in New Issue
Block a user