From c42af6d2ca374c3e209a0e18bde7a5bd1276483b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Fri, 15 Apr 2022 16:32:39 +0300 Subject: [PATCH] :sparkles: feature: support adding queries to RedirectToRoute (#1858) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support adding queries for RedirectToRoute method. * fix security check. * ✨ feature: support adding queries to RedirectToRoute Co-authored-by: wernerr --- ctx.go | 22 ++++++++++++++-- ctx_test.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/ctx.go b/ctx.go index e78126db46..39989c06ee 100644 --- a/ctx.go +++ b/ctx.go @@ -7,6 +7,7 @@ package fiber import ( "bytes" "context" + "encoding/json" "encoding/xml" "errors" "fmt" @@ -23,8 +24,6 @@ import ( "text/template" "time" - "encoding/json" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" "github.com/gofiber/fiber/v2/internal/dictpool" "github.com/gofiber/fiber/v2/internal/schema" @@ -1163,11 +1162,30 @@ func (c *Ctx) GetRouteURL(routeName string, params Map) (string, error) { // RedirectToRoute to the Route registered in the app with appropriate parameters // If status is not specified, status defaults to 302 Found. +// If you want to send queries to route, you must add "queries" key typed as map[string]string to params. 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 } + + // Check queries + if queries, ok := params["queries"].(map[string]string); ok { + queryText := bytebufferpool.Get() + defer bytebufferpool.Put(queryText) + + i := 1 + for k, v := range queries { + _, _ = queryText.WriteString(k + "=" + v) + + if i != len(queries) { + _, _ = queryText.WriteString("&") + } + i++ + } + + return c.Redirect(location+"?"+queryText.String(), status...) + } return c.Redirect(location, status...) } diff --git a/ctx_test.go b/ctx_test.go index 0020b1fd93..07500245eb 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -18,6 +18,7 @@ import ( "io/ioutil" "mime/multipart" "net/http/httptest" + "net/url" "os" "reflect" "strconv" @@ -2254,6 +2255,28 @@ func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } +// go test -run Test_Ctx_RedirectToRouteWithParams +func Test_Ctx_RedirectToRouteWithQueries(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", + "queries": map[string]string{"data[0][name]": "john", "data[0][age]": "10", "test": "doe"}, + }) + utils.AssertEqual(t, 302, c.Response().StatusCode()) + // analysis of query parameters with url parsing, since a map pass is always randomly ordered + location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) + utils.AssertEqual(t, nil, err, "url.Parse(location)") + utils.AssertEqual(t, "/user/fiber", location.Path) + utils.AssertEqual(t, url.Values{"data[0][name]": []string{"john"}, "data[0][age]": []string{"10"}, "test": []string{"doe"}}, location.Query()) +} + // go test -run Test_Ctx_RedirectToRouteWithOptionalParams func Test_Ctx_RedirectToRouteWithOptionalParams(t *testing.T) { t.Parallel() @@ -2543,6 +2566,55 @@ func Benchmark_Ctx_RenderWithLocalsAndBinding(b *testing.B) { utils.AssertEqual(b, "

Hello, World! Test

", string(c.Response().Body())) } +func Benchmark_Ctx_RedirectToRoute(b *testing.B) { + 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) + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + c.RedirectToRoute("user", Map{ + "name": "fiber", + }) + } + + utils.AssertEqual(b, 302, c.Response().StatusCode()) + utils.AssertEqual(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) +} + +func Benchmark_Ctx_RedirectToRouteWithQueries(b *testing.B) { + 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) + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + c.RedirectToRoute("user", Map{ + "name": "fiber", + "queries": map[string]string{"a": "a", "b": "b"}, + }) + } + + utils.AssertEqual(b, 302, c.Response().StatusCode()) + // analysis of query parameters with url parsing, since a map pass is always randomly ordered + location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) + utils.AssertEqual(b, nil, err, "url.Parse(location)") + utils.AssertEqual(b, "/user/fiber", location.Path) + utils.AssertEqual(b, url.Values{"a": []string{"a"}, "b": []string{"b"}}, location.Query()) +} + func Benchmark_Ctx_RenderLocals(b *testing.B) { engine := &testTemplateEngine{} err := engine.Load()