From 46df746b01b8080451aec0c3cd5e77f2afcf15c1 Mon Sep 17 00:00:00 2001 From: JIeJaitt <498938874@qq.com> Date: Tue, 25 Feb 2025 11:11:31 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20Add=20how=20to=20use?= =?UTF-8?q?=20NewCtxFunc=20and=20middleware=20at=20the=20same=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_test.go | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ docs/api/app.md | 82 ++++++++++++++++++++++++++---------------- 2 files changed, 146 insertions(+), 30 deletions(-) diff --git a/app_test.go b/app_test.go index 97d48fce4e..c4596235e8 100644 --- a/app_test.go +++ b/app_test.go @@ -1941,3 +1941,97 @@ func Benchmark_Ctx_AcquireReleaseFlow(b *testing.B) { } }) } + +type Response struct { + Code int `json:"code"` + Data any `json:"data"` + Message string `json:"message"` +} + +type testCustomCtx struct { + *DefaultCtx +} + +func (c *testCustomCtx) JSON(data any, ctype ...string) error { + return c.DefaultCtx.JSON(Response{ + Code: StatusOK, + Data: data, + Message: utils.StatusMessage(StatusOK), + }, ctype...) +} + +func (c *testCustomCtx) Next() error { + // Increment handler index + c.indexHandler++ + + // Did we execute all route handlers? + if c.indexHandler < len(c.route.Handlers) { + // Continue route stack + return c.route.Handlers[c.indexHandler](c) + } + + // Continue handler stack + _, err := c.app.nextCustom(c) + return err +} + +func Test_App_CustomCtx_With_Use(t *testing.T) { + t.Parallel() + + t.Run("without middleware", func(t *testing.T) { + app := New() + app.NewCtxFunc(func(app *App) CustomCtx { + return &testCustomCtx{ + DefaultCtx: NewDefaultCtx(app), + } + }) + + app.Get("/test", func(c Ctx) error { + return c.JSON(Map{"a": "b"}) + }) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + expected := `{"code":200,"data":{"a":"b"},"message":"OK"}` + require.Equal(t, expected, string(body)) + }) + + t.Run("with middleware", func(t *testing.T) { + app := New() + app.NewCtxFunc(func(app *App) CustomCtx { + return &testCustomCtx{ + DefaultCtx: NewDefaultCtx(app), + } + }) + + app.Use(func(c Ctx) error { + t.Logf("Before Next() - context type: %T", c) + err := c.Next() + t.Logf("After Next() - context type: %T", c) + return err + }) + + app.Get("/test", func(c Ctx) error { + if _, ok := c.(*testCustomCtx); !ok { + t.Logf("Handler received context of type: %T", c) + } + return c.JSON(Map{"a": "b"}) + }) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + expected := `{"code":200,"data":{"a":"b"},"message":"OK"}` + require.Equal(t, expected, string(body), + "Custom context JSON format is lost when using middleware") + }) +} diff --git a/docs/api/app.md b/docs/api/app.md index 23171a24a3..ce495344b6 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -556,6 +556,56 @@ func main() { } ``` +:::tip Using with Middleware +When using `NewCtxFunc` together with middleware (`app.Use`), you need to override the `Next()` method in your custom context to preserve the context type throughout the middleware chain: + +```go +type CustomCtx struct { + fiber.DefaultCtx +} + +// Override Next method to preserve CustomCtx type +func (c *CustomCtx) Next() error { + // Increment handler index + c.indexHandler++ + + // Did we execute all route handlers? + if c.indexHandler < len(c.route.Handlers) { + // Continue route stack + return c.route.Handlers[c.indexHandler](c) + } + + // Continue handler stack + _, err := c.app.nextCustom(c) + return err +} + +func main() { + app := fiber.New() + + // Set custom context + app.NewCtxFunc(func(app *fiber.App) fiber.CustomCtx { + return &CustomCtx{ + DefaultCtx: *fiber.NewDefaultCtx(app), + } + }) + + // Use middleware + app.Use(func(c fiber.Ctx) error { + fmt.Printf("Context type in middleware: %T\n", c) + return c.Next() + }) + + // Route handler + app.Get("/", func(c fiber.Ctx) error { + return c.JSON(fiber.Map{"hello": "world"}) + }) +} +``` + +This ensures that your custom context type is maintained throughout the entire request lifecycle, including middleware execution. +::: + ## RegisterCustomBinder You can register custom binders to use with [`Bind().Custom("name")`](bind.md#custom). They should be compatible with the `CustomBinder` interface. @@ -730,34 +780,6 @@ func (app *App) RebuildTree() *App ### Example Usage -Here’s an example of how to define and register routes dynamically: - -```go title="Example" -package main - -import ( - "log" - - "github.com/gofiber/fiber/v3" -) - -func main() { - app := fiber.New() - - app.Get("/define", func(c fiber.Ctx) error { - // Define a new route dynamically - app.Get("/dynamically-defined", func(c fiber.Ctx) error { - return c.SendStatus(fiber.StatusOK) - }) - - // Rebuild the route tree to register the new route - app.RebuildTree() - - return c.SendStatus(fiber.StatusOK) - }) - - log.Fatal(app.Listen(":3000")) -} -``` +Here's an example of how to define and register routes dynamically: -In this example, a new route is defined and then `RebuildTree()` is called to ensure the new route is registered and available. +``` \ No newline at end of file From 320efdd017d12572128eec335ebb246328e830fb Mon Sep 17 00:00:00 2001 From: JIeJaitt <498938874@qq.com> Date: Tue, 25 Feb 2025 13:16:30 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=9A=A8=20Test:=20Improve=20CustomCtx?= =?UTF-8?q?=20JSON=20serialization=20test=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app_test.go b/app_test.go index c4596235e8..48cd5d604a 100644 --- a/app_test.go +++ b/app_test.go @@ -1943,9 +1943,9 @@ func Benchmark_Ctx_AcquireReleaseFlow(b *testing.B) { } type Response struct { - Code int `json:"code"` Data any `json:"data"` Message string `json:"message"` + Code int `json:"code"` } type testCustomCtx struct { @@ -1979,6 +1979,8 @@ func Test_App_CustomCtx_With_Use(t *testing.T) { t.Parallel() t.Run("without middleware", func(t *testing.T) { + t.Parallel() + app := New() app.NewCtxFunc(func(app *App) CustomCtx { return &testCustomCtx{ @@ -1997,11 +1999,13 @@ func Test_App_CustomCtx_With_Use(t *testing.T) { body, err := io.ReadAll(resp.Body) require.NoError(t, err) - expected := `{"code":200,"data":{"a":"b"},"message":"OK"}` + expected := `{"data":{"a":"b"},"message":"OK","code":200}` require.Equal(t, expected, string(body)) }) t.Run("with middleware", func(t *testing.T) { + t.Parallel() + app := New() app.NewCtxFunc(func(app *App) CustomCtx { return &testCustomCtx{ @@ -2030,7 +2034,7 @@ func Test_App_CustomCtx_With_Use(t *testing.T) { body, err := io.ReadAll(resp.Body) require.NoError(t, err) - expected := `{"code":200,"data":{"a":"b"},"message":"OK"}` + expected := `{"data":{"a":"b"},"message":"OK","code":200}` require.Equal(t, expected, string(body), "Custom context JSON format is lost when using middleware") }) From c9e6f8aa5830a0ad4fd911c641532bc648522eaf Mon Sep 17 00:00:00 2001 From: JIeJaitt <498938874@qq.com> Date: Tue, 25 Feb 2025 13:20:50 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20Add=20example=20for?= =?UTF-8?q?=20dynamically=20defining=20routes=20with=20RebuildTree()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api/app.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index ce495344b6..38ca517fab 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -780,6 +780,34 @@ func (app *App) RebuildTree() *App ### Example Usage -Here's an example of how to define and register routes dynamically: +Here’s an example of how to define and register routes dynamically: -``` \ No newline at end of file +```go title="Example" +package main + +import ( + "log" + + "github.com/gofiber/fiber/v3" +) + +func main() { + app := fiber.New() + + app.Get("/define", func(c fiber.Ctx) error { + // Define a new route dynamically + app.Get("/dynamically-defined", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + // Rebuild the route tree to register the new route + app.RebuildTree() + + return c.SendStatus(fiber.StatusOK) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +In this example, a new route is defined and then `RebuildTree()` is called to ensure the new route is registered and available. \ No newline at end of file From ea11d43220b710fd28045664b497df5de3af0524 Mon Sep 17 00:00:00 2001 From: JIeJaitt <498938874@qq.com> Date: Tue, 25 Feb 2025 13:23:36 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20Ensure=20that=20files?= =?UTF-8?q?=20are=20terminated=20with=20a=20single=20line=20break?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api/app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/app.md b/docs/api/app.md index 38ca517fab..a56a1963d6 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -810,4 +810,4 @@ func main() { } ``` -In this example, a new route is defined and then `RebuildTree()` is called to ensure the new route is registered and available. \ No newline at end of file +In this example, a new route is defined and then `RebuildTree()` is called to ensure the new route is registered and available. From e2ad63eb6b90bf0bd230fbb3e275121f9e769409 Mon Sep 17 00:00:00 2001 From: JIeJaitt <498938874@qq.com> Date: Tue, 25 Feb 2025 13:57:37 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=A9=B9=20Fix:=20i/o=20timout=20in=20?= =?UTF-8?q?=20Test=5FProxy=5FDo=5FWithRealURL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware/proxy/proxy_test.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index a810825135..9672194a64 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -501,18 +501,24 @@ func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { // go test -race -run Test_Proxy_Do_WithRealURL func Test_Proxy_Do_WithRealURL(t *testing.T) { t.Parallel() + + _, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error { + return c.SendString("mock response") + }) + app := fiber.New() app.Get("/test", func(c fiber.Ctx) error { - return Do(c, "https://www.google.com") + return Do(c, "http://"+addr) }) - resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) - require.NoError(t, err1) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + require.NoError(t, err) require.Equal(t, fiber.StatusOK, resp.StatusCode) require.Equal(t, "/test", resp.Request.URL.String()) + body, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Contains(t, string(body), "https://www.google.com/") + require.Equal(t, "mock response", string(body)) } // go test -race -run Test_Proxy_Do_WithRedirect