diff --git a/ctx.go b/ctx.go
index c14b3a2773a..ab1a9eaa333 100644
--- a/ctx.go
+++ b/ctx.go
@@ -783,6 +783,14 @@ func (c *Ctx) Next() (err error) {
return err
}
+// RestartRouting instead of going to the next handler. This may be usefull after
+// changing the request path. Note that handlers might be executed again.
+func (c *Ctx) RestartRouting() error {
+ c.indexRoute = -1
+ _, err := c.app.next(c)
+ return err
+}
+
// OriginalURL contains the original request URL.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting to use the value outside the Handler.
diff --git a/ctx_test.go b/ctx_test.go
index 3a790bf315a..1e857143b8e 100644
--- a/ctx_test.go
+++ b/ctx_test.go
@@ -2108,6 +2108,69 @@ func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) {
utils.AssertEqual(t, "
Hello, World!
", string(c.Response().Body()))
}
+// go test -run Test_Ctx_RestartRouting
+func Test_Ctx_RestartRouting(t *testing.T) {
+ app := New()
+ calls := 0
+ app.Get("/", func(c *Ctx) error {
+ calls++
+ if calls < 3 {
+ return c.RestartRouting()
+ }
+ return nil
+ })
+ resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/", nil))
+ utils.AssertEqual(t, nil, err, "app.Test(req)")
+ utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
+ utils.AssertEqual(t, 3, calls, "Number of calls")
+}
+
+// go test -run Test_Ctx_RestartRoutingWithChangedPath
+func Test_Ctx_RestartRoutingWithChangedPath(t *testing.T) {
+ app := New()
+ executedOldHandler := false
+ executedNewHandler := false
+
+ app.Get("/old", func(c *Ctx) error {
+ c.Path("/new")
+ return c.RestartRouting()
+ })
+ app.Get("/old", func(c *Ctx) error {
+ executedOldHandler = true
+ return nil
+ })
+ app.Get("/new", func(c *Ctx) error {
+ executedNewHandler = true
+ return nil
+ })
+
+ resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/old", nil))
+ utils.AssertEqual(t, nil, err, "app.Test(req)")
+ utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
+ utils.AssertEqual(t, false, executedOldHandler, "Executed old handler")
+ utils.AssertEqual(t, true, executedNewHandler, "Executed new handler")
+}
+
+// go test -run Test_Ctx_RestartRoutingWithChangedPathAnd404
+func Test_Ctx_RestartRoutingWithChangedPathAndCatchAll(t *testing.T) {
+ app := New()
+ app.Get("/new", func(c *Ctx) error {
+ return nil
+ })
+ app.Use(func(c *Ctx) error {
+ c.Path("/new")
+ // c.Next() would fail this test as a 404 is returned from the next handler
+ return c.RestartRouting()
+ })
+ app.Use(func(c *Ctx) error {
+ return ErrNotFound
+ })
+
+ resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/old", nil))
+ utils.AssertEqual(t, nil, err, "app.Test(req)")
+ utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
+}
+
type testTemplateEngine struct {
templates *template.Template
}