From 5a81a517be2a08797992d18277101090f30ecd79 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 14 Jun 2021 10:28:49 +0545 Subject: [PATCH 01/27] Add Global Layout for view render --- app.go | 5 +++++ ctx.go | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/app.go b/app.go index eb6f7617a3..a8a123d5ec 100644 --- a/app.go +++ b/app.go @@ -174,6 +174,11 @@ type Config struct { // Default: nil Views Views `json:"-"` + // Views Layout is the global layout for all template render until override on Render function. + // + // Default: "" + ViewsLayout string `json:"views_layout"` + // The amount of time allowed to read the full request including body. // It is reset after the request handler has returned. // The connection's read deadline is reset when the connection opens. diff --git a/ctx.go b/ctx.go index 757ec93ccf..da365f5ab5 100644 --- a/ctx.go +++ b/ctx.go @@ -888,6 +888,12 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { defer bytebufferpool.Put(buf) if c.app.config.Views != nil { + // Render template based on global layout if exists + if len(layouts) == 0 && c.app.config.ViewsLayout != "" { + layouts = []string{ + c.app.config.ViewsLayout, + } + } // Render template from Views if err := c.app.config.Views.Render(buf, name, bind, layouts...); err != nil { return err From 2437d8fbf91078116e223a4093bc9ff0222e0d29 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 14 Jun 2021 14:21:08 +0545 Subject: [PATCH 02/27] Add test case for Views Layout --- .github/testdata/main.tmpl | 1 + ctx_test.go | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .github/testdata/main.tmpl diff --git a/.github/testdata/main.tmpl b/.github/testdata/main.tmpl new file mode 100644 index 0000000000..456d6bb563 --- /dev/null +++ b/.github/testdata/main.tmpl @@ -0,0 +1 @@ +

I'm main

\ No newline at end of file diff --git a/ctx_test.go b/ctx_test.go index c7488b44b2..d465d9d99c 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1639,7 +1639,11 @@ type testTemplateEngine struct { } func (t *testTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { - return t.templates.ExecuteTemplate(w, name, bind) + if len(layout) == 0 { + return t.templates.ExecuteTemplate(w, name, bind) + } + _ = t.templates.ExecuteTemplate(w, name, bind) + return t.templates.ExecuteTemplate(w, layout[0], bind) } func (t *testTemplateEngine) Load() error { @@ -1662,6 +1666,21 @@ func Test_Ctx_Render_Engine(t *testing.T) { utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) } +// go test -run Test_Ctx_Render_Engine +func Test_Ctx_Render_Engine_With_View_Layout(t *testing.T) { + engine := &testTemplateEngine{} + engine.Load() + app := New(Config{ViewsLayout: "main.tmpl"}) + app.config.Views = engine + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + err := c.Render("index.tmpl", Map{ + "Title": "Hello, World!", + }) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

Hello, World!

I'm main

", string(c.Response().Body())) +} + // go test -v -run=^$ -bench=Benchmark_Ctx_Render_Engine -benchmem -count=4 func Benchmark_Ctx_Render_Engine(b *testing.B) { engine := &testTemplateEngine{} From 6f4f14f4f175c662bb321ca8f97c77a906704bc2 Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 14 Jun 2021 11:04:16 +0200 Subject: [PATCH 03/27] Update ctx_test.go --- ctx_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctx_test.go b/ctx_test.go index d465d9d99c..b374f73f01 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1666,7 +1666,7 @@ func Test_Ctx_Render_Engine(t *testing.T) { utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) } -// go test -run Test_Ctx_Render_Engine +// go test -run Test_Ctx_Render_Engine_With_View_Layout func Test_Ctx_Render_Engine_With_View_Layout(t *testing.T) { engine := &testTemplateEngine{} engine.Load() From ad8b4f96fdbf220203e514cc9742cc896ae02d87 Mon Sep 17 00:00:00 2001 From: Sujit Date: Thu, 1 Jul 2021 19:45:40 +0545 Subject: [PATCH 04/27] Add App Name function to pass custom app name --- app.go | 13 ++++++++++--- app_test.go | 7 +++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app.go b/app.go index c4f1ea9cbb..30c0ea12e5 100644 --- a/app.go +++ b/app.go @@ -265,6 +265,11 @@ type Config struct { // Default: false DisableStartupMessage bool `json:"disable_startup_message"` + // This function allows to setup app name for the app + // + // Default: nil + AppName func() string `json:"app_name"` + // Aggressively reduces memory usage at the cost of higher CPU usage // if set to true. // @@ -942,9 +947,11 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { procs = "1" } - mainLogo := cBlack + - " ┌───────────────────────────────────────────────────┐\n" + - " │ " + centerValue(" Fiber v"+Version, 49) + " │\n" + mainLogo := cBlack + " ┌───────────────────────────────────────────────────┐\n" + if app.config.AppName != nil { + mainLogo += " │ " + centerValue(app.config.AppName(), 49) + " │\n" + } + mainLogo += " │ " + centerValue(" Fiber v"+Version, 49) + " │\n" if host == "0.0.0.0" { mainLogo += diff --git a/app_test.go b/app_test.go index 6933e50bbe..3f4b1119a9 100644 --- a/app_test.go +++ b/app_test.go @@ -1261,6 +1261,13 @@ func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) } +func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { + New(Config{Prefork: true, AppName: func() string { + return "Test App v1.0.1" + }}). + startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) +} + func Test_App_Server(t *testing.T) { app := New() From 5b6e8661fe14d1e7f605cbdd7ea21e8a17c2160a Mon Sep 17 00:00:00 2001 From: Sujit Date: Thu, 1 Jul 2021 20:15:39 +0545 Subject: [PATCH 05/27] Remove json tag for function --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 30c0ea12e5..a4eff9f821 100644 --- a/app.go +++ b/app.go @@ -268,7 +268,7 @@ type Config struct { // This function allows to setup app name for the app // // Default: nil - AppName func() string `json:"app_name"` + AppName func() string // Aggressively reduces memory usage at the cost of higher CPU usage // if set to true. From 3ee755781b6e1b8f1d1eabef214193adb01eb432 Mon Sep 17 00:00:00 2001 From: Sujit Date: Thu, 1 Jul 2021 20:42:49 +0545 Subject: [PATCH 06/27] Change func to string --- app.go | 6 +++--- app_test.go | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app.go b/app.go index a4eff9f821..2f9386809d 100644 --- a/app.go +++ b/app.go @@ -268,7 +268,7 @@ type Config struct { // This function allows to setup app name for the app // // Default: nil - AppName func() string + AppName string `json:"app_name"` // Aggressively reduces memory usage at the cost of higher CPU usage // if set to true. @@ -948,8 +948,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } mainLogo := cBlack + " ┌───────────────────────────────────────────────────┐\n" - if app.config.AppName != nil { - mainLogo += " │ " + centerValue(app.config.AppName(), 49) + " │\n" + if app.config.AppName != "" { + mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" } mainLogo += " │ " + centerValue(" Fiber v"+Version, 49) + " │\n" diff --git a/app_test.go b/app_test.go index 3f4b1119a9..b2316e9ecb 100644 --- a/app_test.go +++ b/app_test.go @@ -1262,9 +1262,7 @@ func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { } func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { - New(Config{Prefork: true, AppName: func() string { - return "Test App v1.0.1" - }}). + New(Config{Prefork: true, AppName: "Test App v1.0.1"}). startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) } From 1c6e10266bd8e76dd88988fa978bead37525755c Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 5 Jul 2021 19:39:51 +0545 Subject: [PATCH 07/27] Add test for AppName --- app_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app_test.go b/app_test.go index b2316e9ecb..3ea6045ad1 100644 --- a/app_test.go +++ b/app_test.go @@ -1262,8 +1262,9 @@ func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { } func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { - New(Config{Prefork: true, AppName: "Test App v1.0.1"}). - startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) + app := New(Config{Prefork: true, AppName: "Test App v1.0.1"}) + app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) + utils.AssertEqual(t, "Test App v1.0.1", app.Config().AppName) } func Test_App_Server(t *testing.T) { From a5f67914644a303b0e1911af0a6950d5ddddaf96 Mon Sep 17 00:00:00 2001 From: Sujit Date: Fri, 4 Feb 2022 22:34:29 +0545 Subject: [PATCH 08/27] Add RedirectToRoute and RedirectBack with fallback if referer in header not found --- ctx.go | 40 ++++++++++++++++++++++++++++++++++++++++ ctx_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/ctx.go b/ctx.go index ab1a9eaa33..e9710c55e5 100644 --- a/ctx.go +++ b/ctx.go @@ -1054,6 +1054,46 @@ func (c *Ctx) Redirect(location string, status ...int) error { return 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 { + route := c.App().GetRoute(routeName) + location := "" + for _, segment := range route.routeParser.segs { + location = fmt.Sprintf("%s%s", location, segment.Const) + if segment.IsParam { + if val, ok := params[segment.ParamName]; ok { + location = fmt.Sprintf("%s%s", location, val) + } else { + return errors.New(fmt.Sprintf("redirection failed. No value for param: `%s`", segment.ParamName)) + } + } + } + c.setCanonical(HeaderLocation, location) + if len(status) > 0 { + c.Status(status[0]) + } else { + c.Status(StatusFound) + } + return nil +} + +// 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 + } + c.setCanonical(HeaderLocation, location) + if len(status) > 0 { + c.Status(status[0]) + } else { + c.Status(StatusFound) + } + return nil +} + // 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 1e857143b8..858011381f 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2026,6 +2026,37 @@ func Test_Ctx_Redirect(t *testing.T) { utils.AssertEqual(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) } +// go test -run Test_Ctx_RedirectToRoute +func Test_Ctx_RedirectToRoute(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_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_Render func Test_Ctx_Render(t *testing.T) { t.Parallel() From 82873c838dd06383c5214f01af9b47920e435f70 Mon Sep 17 00:00:00 2001 From: Sujit Date: Fri, 4 Feb 2022 23:03:24 +0545 Subject: [PATCH 09/27] replace errors.New with fmt.Errorf --- ctx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index e9710c55e5..fa2ef2dda7 100644 --- a/ctx.go +++ b/ctx.go @@ -1065,7 +1065,7 @@ func (c *Ctx) RedirectToRoute(routeName string, params Map, status ...int) error if val, ok := params[segment.ParamName]; ok { location = fmt.Sprintf("%s%s", location, val) } else { - return errors.New(fmt.Sprintf("redirection failed. No value for param: `%s`", segment.ParamName)) + return fmt.Errorf("redirection failed. No value for param: `%s`", segment.ParamName) } } } From 8e8d92e7214b28f97b7d5bf8fcbec8a83cff83ed Mon Sep 17 00:00:00 2001 From: Sujit Date: Fri, 4 Feb 2022 23:04:50 +0545 Subject: [PATCH 10/27] simplified code --- ctx.go | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/ctx.go b/ctx.go index fa2ef2dda7..68cee9b64f 100644 --- a/ctx.go +++ b/ctx.go @@ -1044,7 +1044,7 @@ func (c *Ctx) Range(size int) (rangeData Range, err error) { // Redirect to the URL derived from the specified path, with specified status. // If status is not specified, status defaults to 302 Found. -func (c *Ctx) Redirect(location string, status ...int) error { +func (c *Ctx) redirect(location string, status ...int) error { c.setCanonical(HeaderLocation, location) if len(status) > 0 { c.Status(status[0]) @@ -1054,6 +1054,12 @@ func (c *Ctx) Redirect(location string, status ...int) error { return nil } +// Redirect to the URL derived from the specified path, with specified status. +// If status is not specified, status defaults to 302 Found. +func (c *Ctx) Redirect(location string, status ...int) error { + return c.redirect(location, status...) +} + // 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 { @@ -1069,13 +1075,7 @@ func (c *Ctx) RedirectToRoute(routeName string, params Map, status ...int) error } } } - c.setCanonical(HeaderLocation, location) - if len(status) > 0 { - c.Status(status[0]) - } else { - c.Status(StatusFound) - } - return nil + return c.redirect(location, status...) } // RedirectBack to the URL to referer @@ -1085,13 +1085,7 @@ func (c *Ctx) RedirectBack(fallback string, status ...int) error { if location == "" { location = fallback } - c.setCanonical(HeaderLocation, location) - if len(status) > 0 { - c.Status(status[0]) - } else { - c.Status(StatusFound) - } - return nil + return c.redirect(location, status...) } // Render a template with data and sends a text/html response. From 0215512199d721f6db519762a9413c43268889b4 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 7 Feb 2022 21:34:58 +0545 Subject: [PATCH 11/27] Add tests for different formats --- ctx.go | 16 ++++++---------- ctx_test.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/ctx.go b/ctx.go index 68cee9b64f..f1ff338944 100644 --- a/ctx.go +++ b/ctx.go @@ -1044,7 +1044,7 @@ func (c *Ctx) Range(size int) (rangeData Range, err error) { // Redirect to the URL derived from the specified path, with specified status. // If status is not specified, status defaults to 302 Found. -func (c *Ctx) redirect(location string, status ...int) error { +func (c *Ctx) Redirect(location string, status ...int) error { c.setCanonical(HeaderLocation, location) if len(status) > 0 { c.Status(status[0]) @@ -1054,12 +1054,6 @@ func (c *Ctx) redirect(location string, status ...int) error { return nil } -// Redirect to the URL derived from the specified path, with specified status. -// If status is not specified, status defaults to 302 Found. -func (c *Ctx) Redirect(location string, status ...int) error { - return c.redirect(location, status...) -} - // 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 { @@ -1071,11 +1065,13 @@ func (c *Ctx) RedirectToRoute(routeName string, params Map, status ...int) error if val, ok := params[segment.ParamName]; ok { location = fmt.Sprintf("%s%s", location, val) } else { - return fmt.Errorf("redirection failed. No value for param: `%s`", segment.ParamName) + if !segment.IsOptional || !segment.IsGreedy { + return fmt.Errorf("redirection failed. No value for param: `%s`", segment.ParamName) + } } } } - return c.redirect(location, status...) + return c.Redirect(location, status...) } // RedirectBack to the URL to referer @@ -1085,7 +1081,7 @@ func (c *Ctx) RedirectBack(fallback string, status ...int) error { if location == "" { location = fallback } - return c.redirect(location, status...) + return c.Redirect(location, status...) } // Render a template with data and sends a text/html response. diff --git a/ctx_test.go b/ctx_test.go index 858011381f..ba405efa5c 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2027,7 +2027,7 @@ func Test_Ctx_Redirect(t *testing.T) { } // go test -run Test_Ctx_RedirectToRoute -func Test_Ctx_RedirectToRoute(t *testing.T) { +func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { t.Parallel() app := New() app.Get("/user/:name", func(c *Ctx) error { @@ -2043,6 +2043,38 @@ func Test_Ctx_RedirectToRoute(t *testing.T) { utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } +// go test -run Test_Ctx_RedirectToRoute +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_RedirectToRoute +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_RedirectBack func Test_Ctx_RedirectBack(t *testing.T) { t.Parallel() @@ -2057,6 +2089,25 @@ func Test_Ctx_RedirectBack(t *testing.T) { utils.AssertEqual(t, "/", string(c.Response().Header.Peek(HeaderLocation))) } +// go test -run Test_Ctx_RedirectBack +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() From 9013dc589aa3ca9f334cc7474c2167150461cb62 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 7 Feb 2022 23:21:56 +0545 Subject: [PATCH 12/27] Add method to get route location and add benchmarks --- ctx.go | 29 ++++++++++++++++++----------- ctx_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/ctx.go b/ctx.go index f1ff338944..dfe1c0e3e7 100644 --- a/ctx.go +++ b/ctx.go @@ -1054,23 +1054,30 @@ func (c *Ctx) Redirect(location string, status ...int) error { return 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 { - route := c.App().GetRoute(routeName) - location := "" +// get URL location from route using parameters +func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { + var locationBuilder strings.Builder for _, segment := range route.routeParser.segs { - location = fmt.Sprintf("%s%s", location, segment.Const) if segment.IsParam { - if val, ok := params[segment.ParamName]; ok { - location = fmt.Sprintf("%s%s", location, val) - } else { - if !segment.IsOptional || !segment.IsGreedy { - return fmt.Errorf("redirection failed. No value for param: `%s`", segment.ParamName) + for key, val := range params { + if key == segment.ParamName || segment.IsGreedy { + locationBuilder.WriteString(fmt.Sprintf("%s", val)) } } + } else { + locationBuilder.WriteString(segment.Const) } } + return locationBuilder.String(), 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...) } diff --git a/ctx_test.go b/ctx_test.go index ba405efa5c..8ef3a84c34 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2075,6 +2075,23 @@ func Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue(t *testing.T) { utils.AssertEqual(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) } +// go test -run Test_Ctx_RedirectToRoute +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() @@ -2320,6 +2337,19 @@ func Benchmark_Ctx_Render_Engine(b *testing.B) { utils.AssertEqual(b, "

Hello, World!

", 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 { From e716d237fc0af30117f4b8d07bf3c9ab936091af Mon Sep 17 00:00:00 2001 From: Sujit Date: Tue, 8 Feb 2022 00:50:41 +0545 Subject: [PATCH 13/27] Add ToString function --- ctx.go | 10 ++++---- utils/convert.go | 56 +++++++++++++++++++++++++++++++++++++++++++ utils/convert_test.go | 18 ++++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/ctx.go b/ctx.go index dfe1c0e3e7..1758accf41 100644 --- a/ctx.go +++ b/ctx.go @@ -1056,19 +1056,21 @@ func (c *Ctx) Redirect(location string, status ...int) error { // get URL location from route using parameters func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { - var locationBuilder strings.Builder + buf := bytebufferpool.Get() for _, segment := range route.routeParser.segs { if segment.IsParam { for key, val := range params { if key == segment.ParamName || segment.IsGreedy { - locationBuilder.WriteString(fmt.Sprintf("%s", val)) + buf.WriteString(utils.ToString(val)) } } } else { - locationBuilder.WriteString(segment.Const) + buf.WriteString(segment.Const) } } - return locationBuilder.String(), nil + location := buf.String() + bytebufferpool.Put(buf) + return location, nil } // RedirectToRoute to the Route registered in the app with appropriate parameters diff --git a/utils/convert.go b/utils/convert.go index ae99723438..10f13d4421 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -5,9 +5,13 @@ package utils import ( + "errors" + "fmt" + "log" "reflect" "strconv" "strings" + "time" "unsafe" ) @@ -83,3 +87,55 @@ 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 { + if len(timeFormat) > 1 { + log.SetFlags(log.Llongfile | log.LstdFlags) + log.Println(errors.New(fmt.Sprintf("timeFormat's length should be one"))) + } + 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) == 1 { + return v.Format(timeFormat[0]) + } + return v.Format("2006-01-02 15:04:05") + case fmt.Stringer: + return v.String() + case reflect.Value: + return ToString(v.Interface(), timeFormat...) + default: + return "" + } +} diff --git a/utils/convert_test.go b/utils/convert_test.go index d788dead42..59ce625e53 100644 --- a/utils/convert_test.go +++ b/utils/convert_test.go @@ -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) + } +} From d69475241645922bf59cc40ef75875518ea79333 Mon Sep 17 00:00:00 2001 From: Sujit Date: Tue, 8 Feb 2022 13:27:55 +0545 Subject: [PATCH 14/27] Fix error --- ctx.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ctx.go b/ctx.go index 1758accf41..d23f376fda 100644 --- a/ctx.go +++ b/ctx.go @@ -1061,11 +1061,15 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { if segment.IsParam { for key, val := range params { if key == segment.ParamName || segment.IsGreedy { - buf.WriteString(utils.ToString(val)) + _, err := buf.WriteString(utils.ToString(val)) + if err != nil { + return "", err + } } } } else { - buf.WriteString(segment.Const) + _, err := buf.WriteString(segment.Const) + return "", err } } location := buf.String() From 05dd7ba7af5aa98688a5d3e4a66315e793f37300 Mon Sep 17 00:00:00 2001 From: Sujit Date: Tue, 8 Feb 2022 13:41:23 +0545 Subject: [PATCH 15/27] rearrange case for fmt.Stringer --- utils/convert.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/utils/convert.go b/utils/convert.go index 10f13d4421..699d8f321f 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -5,7 +5,6 @@ package utils import ( - "errors" "fmt" "log" "reflect" @@ -92,7 +91,7 @@ func ByteSize(bytes uint64) string { func ToString(arg interface{}, timeFormat ...string) string { if len(timeFormat) > 1 { log.SetFlags(log.Llongfile | log.LstdFlags) - log.Println(errors.New(fmt.Sprintf("timeFormat's length should be one"))) + log.Println(fmt.Errorf("timeFormat's length should be one")) } var tmp = reflect.Indirect(reflect.ValueOf(arg)).Interface() switch v := tmp.(type) { @@ -131,10 +130,10 @@ func ToString(arg interface{}, timeFormat ...string) string { return v.Format(timeFormat[0]) } return v.Format("2006-01-02 15:04:05") - case fmt.Stringer: - return v.String() case reflect.Value: return ToString(v.Interface(), timeFormat...) + case fmt.Stringer: + return v.String() default: return "" } From a9bc1f618540796d9a11e27845ef2a419b1b65e4 Mon Sep 17 00:00:00 2001 From: Sujit Date: Tue, 8 Feb 2022 16:01:27 +0545 Subject: [PATCH 16/27] Fix bug for error return --- ctx.go | 5 ++++- ctx_test.go | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ctx.go b/ctx.go index d23f376fda..4d938d86f9 100644 --- a/ctx.go +++ b/ctx.go @@ -1063,13 +1063,16 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { if key == segment.ParamName || segment.IsGreedy { _, err := buf.WriteString(utils.ToString(val)) if err != nil { + fmt.Println(err) return "", err } } } } else { _, err := buf.WriteString(segment.Const) - return "", err + if err != nil { + return "", err + } } } location := buf.String() diff --git a/ctx_test.go b/ctx_test.go index 8ef3a84c34..25fd42e608 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2026,7 +2026,7 @@ func Test_Ctx_Redirect(t *testing.T) { utils.AssertEqual(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) } -// go test -run Test_Ctx_RedirectToRoute +// go test -run Test_Ctx_RedirectToRouteWithParams func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { t.Parallel() app := New() @@ -2043,7 +2043,7 @@ func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } -// go test -run Test_Ctx_RedirectToRoute +// go test -run Test_Ctx_RedirectToRouteWithOptionalParams func Test_Ctx_RedirectToRouteWithOptionalParams(t *testing.T) { t.Parallel() app := New() @@ -2060,7 +2060,7 @@ func Test_Ctx_RedirectToRouteWithOptionalParams(t *testing.T) { utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } -// go test -run Test_Ctx_RedirectToRoute +// go test -run Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue func Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue(t *testing.T) { t.Parallel() app := New() @@ -2075,7 +2075,7 @@ func Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue(t *testing.T) { utils.AssertEqual(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) } -// go test -run Test_Ctx_RedirectToRoute +// go test -run Test_Ctx_RedirectToRouteWithGreedyParameters func Test_Ctx_RedirectToRouteWithGreedyParameters(t *testing.T) { t.Parallel() app := New() @@ -2106,7 +2106,7 @@ func Test_Ctx_RedirectBack(t *testing.T) { utils.AssertEqual(t, "/", string(c.Response().Header.Peek(HeaderLocation))) } -// go test -run Test_Ctx_RedirectBack +// go test -run Test_Ctx_RedirectBackWithReferer func Test_Ctx_RedirectBackWithReferer(t *testing.T) { t.Parallel() app := New() From 3075c02d6cf0c2f8ba68482af0c469450bca45c9 Mon Sep 17 00:00:00 2001 From: Sujit Date: Tue, 8 Feb 2022 17:42:56 +0545 Subject: [PATCH 17/27] Lock latest route for app.Name(namee string) --- app.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index 17ca68d106..a3c440850d 100644 --- a/app.go +++ b/app.go @@ -567,12 +567,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 } From 245148ef573be92e18a984fd2b01107a5acbb58e Mon Sep 17 00:00:00 2001 From: Sujit Date: Tue, 8 Feb 2022 17:57:37 +0545 Subject: [PATCH 18/27] decreasing timeout for client test with timeout --- client_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() } From 4591b66280d051bbebc6438f63232c862872dd49 Mon Sep 17 00:00:00 2001 From: Sujit Date: Wed, 9 Feb 2022 13:39:11 +0545 Subject: [PATCH 19/27] remove println and adjust condition to > 0 --- ctx.go | 1 - utils/convert.go | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ctx.go b/ctx.go index 2641834e85..efb9b312f1 100644 --- a/ctx.go +++ b/ctx.go @@ -1069,7 +1069,6 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { if key == segment.ParamName || segment.IsGreedy { _, err := buf.WriteString(utils.ToString(val)) if err != nil { - fmt.Println(err) return "", err } } diff --git a/utils/convert.go b/utils/convert.go index 699d8f321f..32b04b7cda 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -6,7 +6,6 @@ package utils import ( "fmt" - "log" "reflect" "strconv" "strings" @@ -89,10 +88,6 @@ func ByteSize(bytes uint64) string { // ToString Change arg to string func ToString(arg interface{}, timeFormat ...string) string { - if len(timeFormat) > 1 { - log.SetFlags(log.Llongfile | log.LstdFlags) - log.Println(fmt.Errorf("timeFormat's length should be one")) - } var tmp = reflect.Indirect(reflect.ValueOf(arg)).Interface() switch v := tmp.(type) { case int: @@ -126,7 +121,7 @@ func ToString(arg interface{}, timeFormat ...string) string { case float64: return strconv.FormatFloat(v, 'f', -1, 64) case time.Time: - if len(timeFormat) == 1 { + if len(timeFormat) > 0 { return v.Format(timeFormat[0]) } return v.Format("2006-01-02 15:04:05") From b6899ec3358f7f7bd98c67c2cc2182fe882c1ae6 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 21 Mar 2022 21:49:38 +0545 Subject: [PATCH 20/27] Change name to get route url --- ctx.go | 6 +++--- ctx_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ctx.go b/ctx.go index 126c7ddd8b..68d056bf62 100644 --- a/ctx.go +++ b/ctx.go @@ -1079,8 +1079,8 @@ func (c *Ctx) Bind(vars Map) error { return nil } -// get URL location from route using parameters -func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { +// GetRouteURL get URL location from route using parameters +func (c *Ctx) GetRouteURL(route Route, params Map) (string, error) { buf := bytebufferpool.Get() for _, segment := range route.routeParser.segs { if segment.IsParam { @@ -1107,7 +1107,7 @@ func (c *Ctx) getLocationFromRoute(route Route, 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. func (c *Ctx) RedirectToRoute(routeName string, params Map, status ...int) error { - location, err := c.getLocationFromRoute(c.App().GetRoute(routeName), params) + location, err := c.GetRouteURL(c.App().GetRoute(routeName), params) if err != nil { return err } diff --git a/ctx_test.go b/ctx_test.go index ee5520b33e..0f2e7d0f40 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2552,7 +2552,7 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { return c.SendString(c.Params("name")) }).Name("User") for n := 0; n < b.N; n++ { - c.getLocationFromRoute(app.GetRoute("User"), Map{"name": "fiber"}) + c.GetRouteURL(app.GetRoute("User"), Map{"name": "fiber"}) } } From c60d0239a0e26e83dd54c0ac72fb7bbce1869f96 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 21 Mar 2022 21:59:52 +0545 Subject: [PATCH 21/27] Change name to get route url --- ctx.go | 11 ++++++++--- ctx_test.go | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ctx.go b/ctx.go index 68d056bf62..5c1b40cec9 100644 --- a/ctx.go +++ b/ctx.go @@ -1079,8 +1079,8 @@ func (c *Ctx) Bind(vars Map) error { return nil } -// GetRouteURL get URL location from route using parameters -func (c *Ctx) GetRouteURL(route Route, params Map) (string, error) { +// getLocationFromRoute 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 { @@ -1104,10 +1104,15 @@ func (c *Ctx) GetRouteURL(route Route, params Map) (string, error) { return location, nil } +// GetRouteURL get URL location from route using parameters +func (c *Ctx) 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. func (c *Ctx) RedirectToRoute(routeName string, params Map, status ...int) error { - location, err := c.GetRouteURL(c.App().GetRoute(routeName), params) + location, err := c.getLocationFromRoute(c.App().GetRoute(routeName), params) if err != nil { return err } diff --git a/ctx_test.go b/ctx_test.go index 0f2e7d0f40..6ff6660e27 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2552,10 +2552,24 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { return c.SendString(c.Params("name")) }).Name("User") for n := 0; n < b.N; n++ { - c.GetRouteURL(app.GetRoute("User"), Map{"name": "fiber"}) + c.getLocationFromRoute(app.GetRoute("User"), Map{"name": "fiber"}) } } +// go test -run Test_Ctx_Get_Location_From_Route_name +func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { + 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") + + location, err := c.GetRouteURL("User", Map{"name": "fiber"}) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/user/fiber", location) +} + type errorTemplateEngine struct{} func (t errorTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { From 25c401fbcbad7d38fdf62e67c860839913d1573b Mon Sep 17 00:00:00 2001 From: Sujit Baniya Date: Tue, 22 Mar 2022 08:48:38 +0545 Subject: [PATCH 22/27] Update ctx.go Co-authored-by: hi019 <65871571+hi019@users.noreply.github.com> --- ctx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index 5c1b40cec9..926e6270a0 100644 --- a/ctx.go +++ b/ctx.go @@ -1104,7 +1104,7 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { return location, nil } -// GetRouteURL get URL location from route using parameters +// GetRouteURL generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831" func (c *Ctx) GetRouteURL(routeName string, params Map) (string, error) { return c.getLocationFromRoute(c.App().GetRoute(routeName), params) } From dda05ce144772fa878a70db803c139c2c78e8d5f Mon Sep 17 00:00:00 2001 From: Sujit Date: Sun, 29 May 2022 21:54:40 +0545 Subject: [PATCH 23/27] Fix bug on getting url for optional and greedy params --- ctx.go | 24 +++++++++++++++--------- ctx_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/ctx.go b/ctx.go index 0cc1b5a7f9..ffec03dd76 100644 --- a/ctx.go +++ b/ctx.go @@ -1143,17 +1143,24 @@ func (c *Ctx) Bind(vars Map) error { // getLocationFromRoute get URL location from route using parameters func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { buf := bytebufferpool.Get() + optionalParamCount := 0 + for key, val := range params { + if key == "*" { + optionalParamCount++ + key = key + utils.ToString(optionalParamCount) + params[key] = val + } + } 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 - } + for key, val := range params { + if segment.IsParam && key == segment.ParamName { + _, err := buf.WriteString(utils.ToString(val)) + if err != nil { + return "", err } } - } else { + } + if !segment.IsParam { _, err := buf.WriteString(segment.Const) if err != nil { return "", err @@ -1161,7 +1168,6 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { } } location := buf.String() - bytebufferpool.Put(buf) return location, nil } diff --git a/ctx_test.go b/ctx_test.go index 3be4e6766f..e593af531d 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2753,6 +2753,41 @@ func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { utils.AssertEqual(t, "/user/fiber", location) } +// go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy +func Test_Ctx_Get_Location_From_Route_name_Optional_greedy(t *testing.T) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + app.Get("/:phone/*/send/*", func(c *Ctx) error { + return c.SendString("Phone: " + c.Params("phone") + "\nFirst Param: " + c.Params("*1") + "\nSecond Param: " + c.Params("*2")) + }).Name("SendSms") + + location, err := c.GetRouteURL("SendSms", Map{ + "phone": "23456789", + "*1": "sms", + "*2": "test-msg", + }) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/23456789/sms/send/test-msg", location) +} + +// go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param +func Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param(t *testing.T) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + app.Get("/:phone/*/send", func(c *Ctx) error { + return c.SendString("Phone: " + c.Params("phone") + "\nFirst Param: " + c.Params("*1")) + }).Name("SendSms") + + location, err := c.GetRouteURL("SendSms", Map{ + "phone": "23456789", + "*": "sms", + }) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/23456789/sms/send", location) +} + type errorTemplateEngine struct{} func (t errorTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { From 96b6196161b1f5959b98d90dfc90b2066531d408 Mon Sep 17 00:00:00 2001 From: Sujit Date: Mon, 30 May 2022 17:15:39 +0545 Subject: [PATCH 24/27] Fix greedy pattern --- ctx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index ffec03dd76..ba8564800b 100644 --- a/ctx.go +++ b/ctx.go @@ -1145,7 +1145,7 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { buf := bytebufferpool.Get() optionalParamCount := 0 for key, val := range params { - if key == "*" { + if key == "*" || key == "+" { optionalParamCount++ key = key + utils.ToString(optionalParamCount) params[key] = val From b8c34599c8960dd69eabc38b53c9136b80963a17 Mon Sep 17 00:00:00 2001 From: wernerr Date: Tue, 31 May 2022 13:33:10 +0200 Subject: [PATCH 25/27] This PR will fix #1921 (comment). The optional and greedy params were not fetched correctly --- ctx.go | 10 +--------- path.go | 2 ++ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/ctx.go b/ctx.go index ba8564800b..a1a38b5849 100644 --- a/ctx.go +++ b/ctx.go @@ -1143,17 +1143,9 @@ func (c *Ctx) Bind(vars Map) error { // getLocationFromRoute get URL location from route using parameters func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { buf := bytebufferpool.Get() - optionalParamCount := 0 - for key, val := range params { - if key == "*" || key == "+" { - optionalParamCount++ - key = key + utils.ToString(optionalParamCount) - params[key] = val - } - } for _, segment := range route.routeParser.segs { for key, val := range params { - if segment.IsParam && key == segment.ParamName { + if (segment.IsParam && key == segment.ParamName) || (segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters)) { _, err := buf.WriteString(utils.ToString(val)) if err != nil { return "", err diff --git a/path.go b/path.go index c847db4414..2f70c7f742 100644 --- a/path.go +++ b/path.go @@ -53,6 +53,8 @@ const ( var ( // slash has a special role, unlike the other parameters it must not be interpreted as a parameter routeDelimiter = []byte{slashDelimiter, '-', '.'} + // list of greedy parameters + greedyParameters = []byte{wildcardParam, plusParam} // list of chars for the parameter recognising parameterStartChars = []byte{wildcardParam, plusParam, paramStarterChar} // list of chars of delimiters and the starting parameter name char From 5c1d67d5bc86ab55c11adfb6142fbc2042f8e2ca Mon Sep 17 00:00:00 2001 From: wernerr Date: Tue, 31 May 2022 13:40:08 +0200 Subject: [PATCH 26/27] This PR will fix #1921 (comment). The optional and greedy params were not fetched correctly --- ctx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index a1a38b5849..7dd39c0be8 100644 --- a/ctx.go +++ b/ctx.go @@ -1145,7 +1145,7 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { buf := bytebufferpool.Get() for _, segment := range route.routeParser.segs { for key, val := range params { - if (segment.IsParam && key == segment.ParamName) || (segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters)) { + if segment.IsParam && (key == segment.ParamName || (segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters))) { _, err := buf.WriteString(utils.ToString(val)) if err != nil { return "", err From 97cdcdc1407eecb2bc84f62ba0ed047dc08db9d1 Mon Sep 17 00:00:00 2001 From: wernerr Date: Tue, 31 May 2022 13:43:29 +0200 Subject: [PATCH 27/27] This PR will fix #1921 (comment). The optional and greedy params were not fetched correctly --- ctx.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ctx.go b/ctx.go index 7dd39c0be8..81c62190c9 100644 --- a/ctx.go +++ b/ctx.go @@ -1160,6 +1160,8 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { } } location := buf.String() + // release buffer + bytebufferpool.Put(buf) return location, nil }