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

✨ v3 (feature): new redirection methods #2014

Merged
merged 32 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
735d8e6
:sparkles: v3 (feature): new redirection methods
efectn Aug 15, 2022
19df0cb
add flash messages
efectn Aug 17, 2022
8dd2e6a
withinput, parsing flash message
efectn Aug 17, 2022
b637dbe
add tests
efectn Aug 17, 2022
5b88c25
add benchmarks
efectn Aug 17, 2022
86af45d
gosec issues
efectn Aug 17, 2022
fbdc979
fix tests
efectn Aug 17, 2022
989196b
fix tests
efectn Aug 17, 2022
7fbf4f6
fix performance issues
efectn Aug 17, 2022
8157c91
fix performance issues
efectn Aug 17, 2022
da38f9b
optimization.
efectn Aug 18, 2022
1fdac6c
better names
efectn Aug 18, 2022
cdccee7
fix tests
efectn Aug 21, 2022
2a07ac7
Merge remote-tracking branch 'origin/master' into v3-new-redirect
efectn Aug 26, 2022
cf48304
Update router.go
efectn Sep 8, 2022
ba282b8
Merge remote-tracking branch 'origin/v3-beta' into v3-new-redirect
efectn Sep 8, 2022
f27561f
fix
efectn Sep 8, 2022
04e2509
fix old messaages with flash messages behavior, add new test case wit…
efectn Sep 8, 2022
a16c3fc
complete some reviews
efectn Sep 15, 2022
ddaefbd
add pool for redirection.
efectn Sep 15, 2022
b2c7fbc
use constant
efectn Sep 15, 2022
da6eb76
update
efectn Sep 15, 2022
8bb04d0
✨ v3 (feature): new redirection methods
ReneWerner87 Sep 16, 2022
e7f3000
fix tests, optimize cookie parsing (9 allocs -> 1 alloc)
efectn Sep 16, 2022
ce62bd7
test case for message includes comma
efectn Sep 16, 2022
8ffe09d
cleanup
efectn Sep 16, 2022
e26b119
optimization.
efectn Sep 16, 2022
817e6d8
some improvements for the redirect feature
ReneWerner87 Sep 18, 2022
31effd6
fix Benchmark_Redirect_Route_WithFlashMessages
efectn Sep 18, 2022
478fe45
some improvements for the redirect feature
ReneWerner87 Sep 19, 2022
8695c65
Update redirect.go
efectn Sep 19, 2022
7eebd54
improve message parsing & test case
efectn Sep 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1006,9 +1006,9 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) {

app.Get("/", func(c Ctx) error {
if c.Request().URI().QueryArgs().Has("foo") {
return c.Redirect("/foo")
return c.Redirect().To("/foo")
}
return c.Redirect("/")
return c.Redirect().To("/")
})
app.Get("/foo", func(c Ctx) error {
return c.SendString("redirect")
Expand Down
56 changes: 9 additions & 47 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type DefaultCtx struct {
matched bool // Non use route matched
viewBindMap *dictpool.Dict // Default view map to bind template engine
bind *Bind // Default bind reference
redirect *Redirect // Default redirect reference
}

// Range data for c.Range
Expand Down Expand Up @@ -824,16 +825,16 @@ func (c *DefaultCtx) Range(size int) (rangeData Range, err error) {
return
}

// Redirect to the URL derived from the specified path, with specified status.
// Redirect returns the Redirect reference.
// Use Redirect().Status() to set custom redirection status code.
// If status is not specified, status defaults to 302 Found.
func (c *DefaultCtx) Redirect(location string, status ...int) error {
c.setCanonical(HeaderLocation, location)
if len(status) > 0 {
c.Status(status[0])
} else {
c.Status(StatusFound)
// You can use Redirect().To(), Redirect().Route() and Redirect().Back() for redirection.
func (c *DefaultCtx) Redirect() *Redirect {
if c.redirect == nil {
c.redirect = newRedirect(c)
}
return nil

return c.redirect
}

// Add vars to default view var map binding to template engine.
Expand Down Expand Up @@ -880,45 +881,6 @@ func (c *DefaultCtx) GetRouteURL(routeName string, params Map) (string, error) {
return c.getLocationFromRoute(c.App().GetRoute(routeName), params)
}

// 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 *DefaultCtx) 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...)
}

// RedirectBack to the URL to referer
// If status is not specified, status defaults to 302 Found.
func (c *DefaultCtx) 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: https://github.com/gofiber/template
func (c *DefaultCtx) Render(name string, bind Map, layouts ...string) error {
Expand Down
16 changes: 5 additions & 11 deletions ctx_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,11 @@ type Ctx interface {
// Range returns a struct containing the type and a slice of ranges.
Range(size int) (rangeData Range, err error)

// Redirect to the URL derived from the specified path, with specified status.
// Redirect returns the Redirect reference.
// Use Redirect().Status() to set custom redirection status code.
// If status is not specified, status defaults to 302 Found.
Redirect(location string, status ...int) error
// You can use Redirect().To(), Redirect().Route() and Redirect().Back() for redirection.
Redirect() *Redirect

// Add vars to default view var map binding to template engine.
// Variables are read by the Render method and may be overwritten.
Expand All @@ -229,15 +231,6 @@ type Ctx interface {
// GetRouteURL generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831"
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.
RedirectToRoute(routeName string, params Map, status ...int) error

// RedirectBack to the URL to referer
// If status is not specified, status defaults to 302 Found.
RedirectBack(fallback string, status ...int) error

// Render a template with data and sends a text/html response.
// We support the following engines: https://github.com/gofiber/template
Render(name string, bind Map, layouts ...string) error
Expand Down Expand Up @@ -431,6 +424,7 @@ func (c *DefaultCtx) release() {
c.route = nil
c.fasthttp = nil
c.bind = nil
c.redirect = nil
if c.viewBindMap != nil {
dictpool.ReleaseDict(c.viewBindMap)
}
Expand Down
179 changes: 0 additions & 179 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"io"
"mime/multipart"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strconv"
Expand Down Expand Up @@ -1904,137 +1903,6 @@ func Test_Ctx_Next_Error(t *testing.T) {
utils.AssertEqual(t, "Works", resp.Header.Get("X-Next-Result"))
}

// go test -run Test_Ctx_Redirect
func Test_Ctx_Redirect(t *testing.T) {
t.Parallel()
app := New()
c := app.NewCtx(&fasthttp.RequestCtx{})

c.Redirect("http://default.com")
utils.AssertEqual(t, 302, c.Response().StatusCode())
utils.AssertEqual(t, "http://default.com", string(c.Response().Header.Peek(HeaderLocation)))

c.Redirect("http://example.com", 301)
utils.AssertEqual(t, 301, c.Response().StatusCode())
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.NewCtx(&fasthttp.RequestCtx{})

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_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.NewCtx(&fasthttp.RequestCtx{})

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()
app := New()
app.Get("/user/:name?", func(c Ctx) error {
return c.JSON(c.Params("name"))
}).Name("user")
c := app.NewCtx(&fasthttp.RequestCtx{})

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.NewCtx(&fasthttp.RequestCtx{})

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.NewCtx(&fasthttp.RequestCtx{})

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.NewCtx(&fasthttp.RequestCtx{})

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.NewCtx(&fasthttp.RequestCtx{})

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()
Expand Down Expand Up @@ -2245,53 +2113,6 @@ func Benchmark_Ctx_RenderWithLocalsAndBindVars(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.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx)

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.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx)

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
2 changes: 1 addition & 1 deletion middleware/expvar/expvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ func New(config ...Config) fiber.Handler {
return nil
}

return c.Redirect("/debug/vars", 302)
return c.Redirect().To("/debug/vars")
}
}
2 changes: 1 addition & 1 deletion middleware/pprof/pprof.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func New(config ...Config) fiber.Handler {
path = "/debug/pprof/"
}

return c.Redirect(path, fiber.StatusFound)
return c.Redirect().To(path)
}
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion middleware/redirect/redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func New(config ...Config) fiber.Handler {
for k, v := range cfg.rulesRegex {
replacer := captureTokens(k, c.Path())
if replacer != nil {
return c.Redirect(replacer.Replace(v), cfg.StatusCode)
return c.Redirect().Status(cfg.StatusCode).To(replacer.Replace(v))
}
}
return c.Next()
Expand Down
Loading