Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feature: support adding queries to RedirectToRoute #1858

Merged
merged 3 commits into from Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 20 additions & 2 deletions ctx.go
Expand Up @@ -7,6 +7,7 @@ package fiber
import (
"bytes"
"context"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
Expand All @@ -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"
Expand Down Expand Up @@ -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...)
}

Expand Down
72 changes: 72 additions & 0 deletions ctx_test.go
Expand Up @@ -18,6 +18,7 @@ import (
"io/ioutil"
"mime/multipart"
"net/http/httptest"
"net/url"
"os"
"reflect"
"strconv"
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -2543,6 +2566,55 @@ func Benchmark_Ctx_RenderWithLocalsAndBinding(b *testing.B) {
utils.AssertEqual(b, "<h1>Hello, World! Test</h1>", 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()
Expand Down