1
0
mirror of https://github.com/labstack/echo.git synced 2024-11-24 08:22:21 +02:00

Proper colon support in reverse (#2416)

* Adds support of the escaped colon in echo.Reverse

---------

Co-authored-by: Mihard <mihard@webird.ru>
This commit is contained in:
Mihard 2023-04-16 20:13:47 +02:00 committed by GitHub
parent de1c798143
commit 7d54690cdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 95 additions and 19 deletions

View File

@ -1517,26 +1517,97 @@ func TestEcho_OnAddRouteHandler(t *testing.T) {
}
func TestEchoReverse(t *testing.T) {
e := New()
dummyHandler := func(Context) error { return nil }
var testCases = []struct {
name string
whenRouteName string
whenParams []interface{}
expect string
}{
{
name: "ok,static with no params",
whenRouteName: "/static",
expect: "/static",
},
{
name: "ok,static with non existent param",
whenRouteName: "/static",
whenParams: []interface{}{"missing param"},
expect: "/static",
},
{
name: "ok, wildcard with no params",
whenRouteName: "/static/*",
expect: "/static/*",
},
{
name: "ok, wildcard with params",
whenRouteName: "/static/*",
whenParams: []interface{}{"foo.txt"},
expect: "/static/foo.txt",
},
{
name: "ok, single param without param",
whenRouteName: "/params/:foo",
expect: "/params/:foo",
},
{
name: "ok, single param with param",
whenRouteName: "/params/:foo",
whenParams: []interface{}{"one"},
expect: "/params/one",
},
{
name: "ok, multi param without params",
whenRouteName: "/params/:foo/bar/:qux",
expect: "/params/:foo/bar/:qux",
},
{
name: "ok, multi param with one param",
whenRouteName: "/params/:foo/bar/:qux",
whenParams: []interface{}{"one"},
expect: "/params/one/bar/:qux",
},
{
name: "ok, multi param with all params",
whenRouteName: "/params/:foo/bar/:qux",
whenParams: []interface{}{"one", "two"},
expect: "/params/one/bar/two",
},
{
name: "ok, multi param + wildcard with all params",
whenRouteName: "/params/:foo/bar/:qux/*",
whenParams: []interface{}{"one", "two", "three"},
expect: "/params/one/bar/two/three",
},
{
name: "ok, backslash is not escaped",
whenRouteName: "/backslash",
whenParams: []interface{}{"test"},
expect: `/a\b/test`,
},
{
name: "ok, escaped colon verbs",
whenRouteName: "/params:customVerb",
whenParams: []interface{}{"PATCH"},
expect: `/params:PATCH`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
e := New()
dummyHandler := func(Context) error { return nil }
e.GET("/static", dummyHandler).Name = "/static"
e.GET("/static/*", dummyHandler).Name = "/static/*"
e.GET("/params/:foo", dummyHandler).Name = "/params/:foo"
e.GET("/params/:foo/bar/:qux", dummyHandler).Name = "/params/:foo/bar/:qux"
e.GET("/params/:foo/bar/:qux/*", dummyHandler).Name = "/params/:foo/bar/:qux/*"
e.GET("/static", dummyHandler).Name = "/static"
e.GET("/static/*", dummyHandler).Name = "/static/*"
e.GET("/params/:foo", dummyHandler).Name = "/params/:foo"
e.GET("/params/:foo/bar/:qux", dummyHandler).Name = "/params/:foo/bar/:qux"
e.GET("/params/:foo/bar/:qux/*", dummyHandler).Name = "/params/:foo/bar/:qux/*"
e.GET("/a\\b/:x", dummyHandler).Name = "/backslash"
e.GET("/params\\::customVerb", dummyHandler).Name = "/params:customVerb"
assert.Equal(t, "/static", e.Reverse("/static"))
assert.Equal(t, "/static", e.Reverse("/static", "missing param"))
assert.Equal(t, "/static/*", e.Reverse("/static/*"))
assert.Equal(t, "/static/foo.txt", e.Reverse("/static/*", "foo.txt"))
assert.Equal(t, "/params/:foo", e.Reverse("/params/:foo"))
assert.Equal(t, "/params/one", e.Reverse("/params/:foo", "one"))
assert.Equal(t, "/params/:foo/bar/:qux", e.Reverse("/params/:foo/bar/:qux"))
assert.Equal(t, "/params/one/bar/:qux", e.Reverse("/params/:foo/bar/:qux", "one"))
assert.Equal(t, "/params/one/bar/two", e.Reverse("/params/:foo/bar/:qux", "one", "two"))
assert.Equal(t, "/params/one/bar/two/three", e.Reverse("/params/:foo/bar/:qux/*", "one", "two", "three"))
assert.Equal(t, tc.expect, e.Reverse(tc.whenRouteName, tc.whenParams...))
})
}
}
func TestEchoReverseHandleHostProperly(t *testing.T) {

View File

@ -159,7 +159,12 @@ func (r *Router) Reverse(name string, params ...interface{}) string {
for _, route := range r.routes {
if route.Name == name {
for i, l := 0, len(route.Path); i < l; i++ {
if (route.Path[i] == ':' || route.Path[i] == '*') && n < ln {
hasBackslash := route.Path[i] == '\\'
if hasBackslash && i+1 < l && route.Path[i+1] == ':' {
i++ // backslash before colon escapes that colon. in that case skip backslash
}
if n < ln && (route.Path[i] == '*' || (!hasBackslash && route.Path[i] == ':')) {
// in case of `*` wildcard or `:` (unescaped colon) param we replace everything till next slash or end of path
for ; i < l && route.Path[i] != '/'; i++ {
}
uri.WriteString(fmt.Sprintf("%v", params[n]))