diff --git a/app.go b/app.go index fcc84effa7..ee695a1223 100644 --- a/app.go +++ b/app.go @@ -569,12 +569,13 @@ func (app *App) Mount(prefix string, fiber *App) Router { // Assign name to specific route. func (app *App) Name(name string) Router { + latestRoute.mu.Lock() if strings.HasPrefix(latestRoute.route.path, latestGroup.prefix) { latestRoute.route.Name = latestGroup.name + name } else { latestRoute.route.Name = name } - + latestRoute.mu.Unlock() return app } diff --git a/client_test.go b/client_test.go index 81480b29ec..f9ab5fdae1 100644 --- a/client_test.go +++ b/client_test.go @@ -953,7 +953,7 @@ func Test_Client_Agent_Timeout(t *testing.T) { go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() a := Get("http://example.com"). - Timeout(time.Millisecond * 100) + Timeout(time.Millisecond * 50) a.HostClient.Dial = func(addr string) (net.Conn, error) { return ln.Dial() } diff --git a/ctx.go b/ctx.go index 96380b3b95..efb9b312f1 100644 --- a/ctx.go +++ b/ctx.go @@ -1060,6 +1060,51 @@ func (c *Ctx) Redirect(location string, status ...int) error { return nil } +// get URL location from route using parameters +func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { + buf := bytebufferpool.Get() + for _, segment := range route.routeParser.segs { + if segment.IsParam { + for key, val := range params { + if key == segment.ParamName || segment.IsGreedy { + _, err := buf.WriteString(utils.ToString(val)) + if err != nil { + return "", err + } + } + } + } else { + _, err := buf.WriteString(segment.Const) + if err != nil { + return "", err + } + } + } + location := buf.String() + bytebufferpool.Put(buf) + return location, nil +} + +// RedirectToRoute to the Route registered in the app with appropriate parameters +// If status is not specified, status defaults to 302 Found. +func (c *Ctx) RedirectToRoute(routeName string, params Map, status ...int) error { + location, err := c.getLocationFromRoute(c.App().GetRoute(routeName), params) + if err != nil { + return err + } + return c.Redirect(location, status...) +} + +// RedirectBack to the URL to referer +// If status is not specified, status defaults to 302 Found. +func (c *Ctx) RedirectBack(fallback string, status ...int) error { + location := c.Get(HeaderReferer) + if location == "" { + location = fallback + } + return c.Redirect(location, status...) +} + // Render a template with data and sends a text/html response. // We support the following engines: html, amber, handlebars, mustache, pug func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { diff --git a/ctx_test.go b/ctx_test.go index d016d61ba8..333bdb6b64 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2035,6 +2035,105 @@ func Test_Ctx_Redirect(t *testing.T) { utils.AssertEqual(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) } +// go test -run Test_Ctx_RedirectToRouteWithParams +func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/:name", func(c *Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + c.RedirectToRoute("user", Map{ + "name": "fiber", + }) + utils.AssertEqual(t, 302, c.Response().StatusCode()) + utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Ctx_RedirectToRouteWithOptionalParams +func Test_Ctx_RedirectToRouteWithOptionalParams(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/:name?", func(c *Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + c.RedirectToRoute("user", Map{ + "name": "fiber", + }) + utils.AssertEqual(t, 302, c.Response().StatusCode()) + utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue +func Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/:name?", func(c *Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + c.RedirectToRoute("user", Map{}) + utils.AssertEqual(t, 302, c.Response().StatusCode()) + utils.AssertEqual(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Ctx_RedirectToRouteWithGreedyParameters +func Test_Ctx_RedirectToRouteWithGreedyParameters(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/+", func(c *Ctx) error { + return c.JSON(c.Params("+")) + }).Name("user") + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + c.RedirectToRoute("user", Map{ + "+": "test/routes", + }) + utils.AssertEqual(t, 302, c.Response().StatusCode()) + utils.AssertEqual(t, "/user/test/routes", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Ctx_RedirectBack +func Test_Ctx_RedirectBack(t *testing.T) { + t.Parallel() + app := New() + app.Get("/", func(c *Ctx) error { + return c.JSON("Home") + }).Name("home") + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.RedirectBack("/") + utils.AssertEqual(t, 302, c.Response().StatusCode()) + utils.AssertEqual(t, "/", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Ctx_RedirectBackWithReferer +func Test_Ctx_RedirectBackWithReferer(t *testing.T) { + t.Parallel() + app := New() + app.Get("/", func(c *Ctx) error { + return c.JSON("Home") + }).Name("home") + app.Get("/back", func(c *Ctx) error { + return c.JSON("Back") + }).Name("back") + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().Header.Set(HeaderReferer, "/back") + c.RedirectBack("/") + utils.AssertEqual(t, 302, c.Response().StatusCode()) + utils.AssertEqual(t, "/back", c.Get(HeaderReferer)) + utils.AssertEqual(t, "/back", string(c.Response().Header.Peek(HeaderLocation))) +} + // go test -run Test_Ctx_Render func Test_Ctx_Render(t *testing.T) { t.Parallel() @@ -2300,6 +2399,19 @@ func Benchmark_Ctx_Render_Engine(b *testing.B) { utils.AssertEqual(b, "