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 REQUEST] - Add RedirectToRoute and RedirectBack #1750

Merged
merged 30 commits into from Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ae0cc9a
Merge pull request #4 from gofiber/master
sujit-baniya May 6, 2021
744fbe2
Merge branch 'gofiber:master' into master
sujit-baniya Jun 2, 2021
75b5c18
Merge branch 'gofiber:master' into master
sujit-baniya Jun 14, 2021
5a81a51
Add Global Layout for view render
sujit-baniya Jun 14, 2021
2437d8f
Add test case for Views Layout
sujit-baniya Jun 14, 2021
6f4f14f
Update ctx_test.go
ReneWerner87 Jun 14, 2021
3812595
Merge branch 'gofiber:master' into master
sujit-baniya Jun 23, 2021
27a01e7
Merge branch 'gofiber:master' into master
sujit-baniya Jun 27, 2021
2f05a64
Merge branch 'gofiber:master' into master
sujit-baniya Jul 1, 2021
ad8b4f9
Add App Name function to pass custom app name
sujit-baniya Jul 1, 2021
5b6e866
Remove json tag for function
sujit-baniya Jul 1, 2021
3ee7557
Change func to string
sujit-baniya Jul 1, 2021
1c6e102
Add test for AppName
sujit-baniya Jul 5, 2021
5dddb64
Merge branch 'master' into master
ReneWerner87 Jul 16, 2021
86ef9ce
Merge branch 'gofiber:master' into master
sujit-baniya Aug 21, 2021
3a367be
Merge branch 'gofiber:master' into master
sujit-baniya Feb 4, 2022
a5f6791
Add RedirectToRoute and RedirectBack with fallback if referer in head…
sujit-baniya Feb 4, 2022
82873c8
replace errors.New with fmt.Errorf
sujit-baniya Feb 4, 2022
8e8d92e
simplified code
sujit-baniya Feb 4, 2022
0215512
Add tests for different formats
sujit-baniya Feb 7, 2022
9013dc5
Add method to get route location and add benchmarks
sujit-baniya Feb 7, 2022
e716d23
Add ToString function
sujit-baniya Feb 7, 2022
d694752
Fix error
sujit-baniya Feb 8, 2022
05dd7ba
rearrange case for fmt.Stringer
sujit-baniya Feb 8, 2022
a9bc1f6
Fix bug for error return
sujit-baniya Feb 8, 2022
9c6a15d
Merge branch 'gofiber:master' into master
sujit-baniya Feb 8, 2022
3075c02
Lock latest route for app.Name(namee string)
sujit-baniya Feb 8, 2022
81455f0
Merge remote-tracking branch 'origin/master'
sujit-baniya Feb 8, 2022
245148e
decreasing timeout for client test with timeout
sujit-baniya Feb 8, 2022
4591b66
remove println and adjust condition to > 0
sujit-baniya Feb 9, 2022
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
3 changes: 2 additions & 1 deletion app.go
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion client_test.go
Expand Up @@ -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() }

Expand Down
45 changes: 45 additions & 0 deletions ctx.go
Expand Up @@ -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 {
Expand Down
112 changes: 112 additions & 0 deletions ctx_test.go
Expand Up @@ -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)))
ReneWerner87 marked this conversation as resolved.
Show resolved Hide resolved
}

// 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) {
ReneWerner87 marked this conversation as resolved.
Show resolved Hide resolved
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()
Expand Down Expand Up @@ -2300,6 +2399,19 @@ func Benchmark_Ctx_Render_Engine(b *testing.B) {
utils.AssertEqual(b, "<h1>Hello, World!</h1>", string(c.Response().Body()))
}

// go test -v -run=^$ -bench=Benchmark_Ctx_Get_Location_From_Route -benchmem -count=4
func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) {
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
app.Get("/user/:name", func(c *Ctx) error {
return c.SendString(c.Params("name"))
}).Name("User")
for n := 0; n < b.N; n++ {
c.getLocationFromRoute(app.GetRoute("User"), Map{"name": "fiber"})
}
}

type errorTemplateEngine struct{}

func (t errorTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error {
Expand Down
50 changes: 50 additions & 0 deletions utils/convert.go
Expand Up @@ -5,9 +5,11 @@
package utils

import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
"unsafe"
)

Expand Down Expand Up @@ -83,3 +85,51 @@ func ByteSize(bytes uint64) string {
result = strings.TrimSuffix(result, ".0")
return result + unit
}

// ToString Change arg to string
func ToString(arg interface{}, timeFormat ...string) string {
var tmp = reflect.Indirect(reflect.ValueOf(arg)).Interface()
switch v := tmp.(type) {
case int:
return strconv.Itoa(v)
case int8:
return strconv.FormatInt(int64(v), 10)
case int16:
return strconv.FormatInt(int64(v), 10)
case int32:
return strconv.FormatInt(int64(v), 10)
case int64:
return strconv.FormatInt(v, 10)
case uint:
return strconv.Itoa(int(v))
case uint8:
return strconv.FormatInt(int64(v), 10)
case uint16:
return strconv.FormatInt(int64(v), 10)
case uint32:
return strconv.FormatInt(int64(v), 10)
case uint64:
return strconv.FormatInt(int64(v), 10)
case string:
return v
case []byte:
return string(v)
case bool:
return strconv.FormatBool(v)
case float32:
return strconv.FormatFloat(float64(v), 'f', -1, 32)
case float64:
return strconv.FormatFloat(v, 'f', -1, 64)
case time.Time:
if len(timeFormat) > 0 {
return v.Format(timeFormat[0])
}
return v.Format("2006-01-02 15:04:05")
case reflect.Value:
return ToString(v.Interface(), timeFormat...)
case fmt.Stringer:
return v.String()
default:
return ""
}
}
18 changes: 18 additions & 0 deletions utils/convert_test.go
Expand Up @@ -61,3 +61,21 @@ func Test_CopyString(t *testing.T) {
res := CopyString("Hello, World!")
AssertEqual(t, "Hello, World!", res)
}

func Test_ToString(t *testing.T) {
t.Parallel()
res := ToString([]byte("Hello, World!"))
AssertEqual(t, "Hello, World!", res)
res = ToString(true)
AssertEqual(t, "true", res)
res = ToString(uint(100))
AssertEqual(t, "100", res)
}

// go test -v -run=^$ -bench=ToString -benchmem -count=2
func Benchmark_ToString(b *testing.B) {
hello := []byte("Hello, World!")
for n := 0; n < b.N; n++ {
ToString(hello)
}
}