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 }