From c2021282918b0e3eb8765c5b5035fd3ef16e33f2 Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Sun, 2 Feb 2025 13:28:40 +0800 Subject: [PATCH 01/11] Add ContextWithBody signatures --- ctx.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ctx.go b/ctx.go index 64deaad9..c029f5e6 100644 --- a/ctx.go +++ b/ctx.go @@ -44,6 +44,8 @@ type ContextWithBody[B any] interface { // ... // }) PathParam(name string) string + PathParamInt(name string) int // If the path parameter is not provided or is not an int, it returns 0. Use [Ctx.PathParamIntErr] if you want to know if the path parameter is erroneous. + PathParamIntErr(name string) (int, error) QueryParam(name string) string QueryParamArr(name string) []string From dca9e252fd7eb5211980d7ef041b7ab1fa0b8e8c Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Sun, 2 Feb 2025 13:29:20 +0800 Subject: [PATCH 02/11] Implement for netHttpContext --- ctx.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ctx.go b/ctx.go index c029f5e6..0c93d1d9 100644 --- a/ctx.go +++ b/ctx.go @@ -8,6 +8,7 @@ import ( "io/fs" "net/http" "net/url" + "strconv" "strings" "time" @@ -222,6 +223,55 @@ func (c netHttpContext[B]) PathParam(name string) string { return c.Req.PathValue(name) } +func (c netHttpContext[B]) PathParamIntErr(name string) (int, error) { + param := c.PathParam(name) + if param == "" { + return 0, PathParamNotFoundError{ParamName: name} + } + + i, err := strconv.Atoi(param) + if err != nil { + return 0, PathParamInvalidTypeError{ + ParamName: name, + ParamValue: param, + ExpectedType: "int", + Err: err, + } + } + + return i, nil +} + +type PathParamNotFoundError struct { + ParamName string +} + +func (e PathParamNotFoundError) Error() string { + return fmt.Errorf("param %s not found", e.ParamName).Error() +} + +type PathParamInvalidTypeError struct { + Err error + ParamName string + ParamValue string + ExpectedType string +} + +func (e PathParamInvalidTypeError) Error() string { + return fmt.Errorf("param %s=%s is not of type %s: %w", e.ParamName, e.ParamValue, e.ExpectedType, e.Err).Error() +} + +// PathParamInt returns the path parameter with the given name as an int. +// If the query parameter does not exist, or if it is not an int, it returns 0. +func (c netHttpContext[B]) PathParamInt(name string) int { + param, err := c.PathParamIntErr(name) + if err != nil { + return 0 + } + + return param +} + func (c netHttpContext[B]) MainLang() string { return strings.Split(c.MainLocale(), "-")[0] } From b4bd161f4799977fd2aef0e0c027a0881ca03cea Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Sun, 2 Feb 2025 15:54:36 +0800 Subject: [PATCH 03/11] Fix go.work --- go.work | 1 + 1 file changed, 1 insertion(+) diff --git a/go.work b/go.work index 37b2f679..7fa00701 100644 --- a/go.work +++ b/go.work @@ -17,6 +17,7 @@ use ( ./examples/petstore ./examples/with-listener ./extra/fuegogin + ./extra/fuegoecho ./extra/markdown ./middleware/basicauth ./middleware/cache From f99a991864227fe1bacbe4b55424de7dd5aa503d Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Sun, 2 Feb 2025 17:10:20 +0800 Subject: [PATCH 04/11] Implement for echoContext and ginContext --- extra/fuegoecho/context.go | 49 ++++++++++++++++++++++++++++++++++++++ extra/fuegogin/context.go | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/extra/fuegoecho/context.go b/extra/fuegoecho/context.go index a21862fa..1b0b6c7d 100644 --- a/extra/fuegoecho/context.go +++ b/extra/fuegoecho/context.go @@ -3,7 +3,9 @@ package fuegoecho import ( "context" "errors" + "fmt" "net/http" + "strconv" "strings" "github.com/go-fuego/fuego" @@ -55,6 +57,53 @@ func (c echoContext[B]) PathParam(name string) string { return c.echoCtx.Param(name) } +func (c echoContext[B]) PathParamIntErr(name string) (int, error) { + param := c.PathParam(name) + if param == "" { + return 0, PathParamNotFoundError{ParamName: name} + } + + i, err := strconv.Atoi(param) + if err != nil { + return 0, PathParamInvalidTypeError{ + ParamName: name, + ParamValue: param, + ExpectedType: "int", + Err: err, + } + } + + return i, nil +} + +type PathParamNotFoundError struct { + ParamName string +} + +func (e PathParamNotFoundError) Error() string { + return fmt.Errorf("param %s not found", e.ParamName).Error() +} + +type PathParamInvalidTypeError struct { + Err error + ParamName string + ParamValue string + ExpectedType string +} + +func (e PathParamInvalidTypeError) Error() string { + return fmt.Errorf("param %s=%s is not of type %s: %w", e.ParamName, e.ParamValue, e.ExpectedType, e.Err).Error() +} + +func (c echoContext[B]) PathParamInt(name string) int { + param, err := c.PathParamIntErr(name) + if err != nil { + return 0 + } + + return param +} + func (c echoContext[B]) MainLang() string { return strings.Split(c.MainLocale(), "-")[0] } diff --git a/extra/fuegogin/context.go b/extra/fuegogin/context.go index c4e8afb1..45cef4ae 100644 --- a/extra/fuegogin/context.go +++ b/extra/fuegogin/context.go @@ -3,7 +3,9 @@ package fuegogin import ( "context" "errors" + "fmt" "net/http" + "strconv" "strings" "github.com/gin-gonic/gin" @@ -55,6 +57,53 @@ func (c ginContext[B]) PathParam(name string) string { return c.ginCtx.Param(name) } +func (c ginContext[B]) PathParamIntErr(name string) (int, error) { + param := c.PathParam(name) + if param == "" { + return 0, PathParamNotFoundError{ParamName: name} + } + + i, err := strconv.Atoi(param) + if err != nil { + return 0, PathParamInvalidTypeError{ + ParamName: name, + ParamValue: param, + ExpectedType: "int", + Err: err, + } + } + + return i, nil +} + +type PathParamNotFoundError struct { + ParamName string +} + +func (e PathParamNotFoundError) Error() string { + return fmt.Errorf("param %s not found", e.ParamName).Error() +} + +type PathParamInvalidTypeError struct { + Err error + ParamName string + ParamValue string + ExpectedType string +} + +func (e PathParamInvalidTypeError) Error() string { + return fmt.Errorf("param %s=%s is not of type %s: %w", e.ParamName, e.ParamValue, e.ExpectedType, e.Err).Error() +} + +func (c ginContext[B]) PathParamInt(name string) int { + param, err := c.PathParamIntErr(name) + if err != nil { + return 0 + } + + return param +} + func (c ginContext[B]) MainLang() string { return strings.Split(c.MainLocale(), "-")[0] } From b249cd0e1ee12f1fd382b9fd2cc71a831f88989b Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Sun, 2 Feb 2025 17:11:03 +0800 Subject: [PATCH 05/11] Implement for MockContext --- mock_context.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mock_context.go b/mock_context.go index 2cf5cabf..6b362059 100644 --- a/mock_context.go +++ b/mock_context.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/url" + "strconv" "strings" "github.com/go-fuego/fuego/internal" @@ -84,6 +85,17 @@ func (m *MockContext[B]) PathParam(name string) string { return m.PathParams[name] } +func (m *MockContext[B]) PathParamIntErr(name string) (int, error) { + return strconv.Atoi(m.PathParams[name]) +} + +func (m *MockContext[B]) PathParamInt(name string) int { + if i, err := m.PathParamIntErr(name); err == nil { + return i + } + return 0 +} + // Request returns the mock request func (m *MockContext[B]) Request() *http.Request { return m.request From c1afff89262514a903e693e5ae5ab254789658e3 Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Sun, 2 Feb 2025 19:31:52 +0800 Subject: [PATCH 06/11] Refactor errors --- extra/fuegoecho/context.go | 24 ++---------------------- extra/fuegogin/context.go | 24 ++---------------------- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/extra/fuegoecho/context.go b/extra/fuegoecho/context.go index 1b0b6c7d..ce73f405 100644 --- a/extra/fuegoecho/context.go +++ b/extra/fuegoecho/context.go @@ -3,7 +3,6 @@ package fuegoecho import ( "context" "errors" - "fmt" "net/http" "strconv" "strings" @@ -60,12 +59,12 @@ func (c echoContext[B]) PathParam(name string) string { func (c echoContext[B]) PathParamIntErr(name string) (int, error) { param := c.PathParam(name) if param == "" { - return 0, PathParamNotFoundError{ParamName: name} + return 0, fuego.PathParamNotFoundError{ParamName: name} } i, err := strconv.Atoi(param) if err != nil { - return 0, PathParamInvalidTypeError{ + return 0, fuego.PathParamInvalidTypeError{ ParamName: name, ParamValue: param, ExpectedType: "int", @@ -76,25 +75,6 @@ func (c echoContext[B]) PathParamIntErr(name string) (int, error) { return i, nil } -type PathParamNotFoundError struct { - ParamName string -} - -func (e PathParamNotFoundError) Error() string { - return fmt.Errorf("param %s not found", e.ParamName).Error() -} - -type PathParamInvalidTypeError struct { - Err error - ParamName string - ParamValue string - ExpectedType string -} - -func (e PathParamInvalidTypeError) Error() string { - return fmt.Errorf("param %s=%s is not of type %s: %w", e.ParamName, e.ParamValue, e.ExpectedType, e.Err).Error() -} - func (c echoContext[B]) PathParamInt(name string) int { param, err := c.PathParamIntErr(name) if err != nil { diff --git a/extra/fuegogin/context.go b/extra/fuegogin/context.go index 45cef4ae..e8463101 100644 --- a/extra/fuegogin/context.go +++ b/extra/fuegogin/context.go @@ -3,7 +3,6 @@ package fuegogin import ( "context" "errors" - "fmt" "net/http" "strconv" "strings" @@ -60,12 +59,12 @@ func (c ginContext[B]) PathParam(name string) string { func (c ginContext[B]) PathParamIntErr(name string) (int, error) { param := c.PathParam(name) if param == "" { - return 0, PathParamNotFoundError{ParamName: name} + return 0, fuego.PathParamNotFoundError{ParamName: name} } i, err := strconv.Atoi(param) if err != nil { - return 0, PathParamInvalidTypeError{ + return 0, fuego.PathParamInvalidTypeError{ ParamName: name, ParamValue: param, ExpectedType: "int", @@ -76,25 +75,6 @@ func (c ginContext[B]) PathParamIntErr(name string) (int, error) { return i, nil } -type PathParamNotFoundError struct { - ParamName string -} - -func (e PathParamNotFoundError) Error() string { - return fmt.Errorf("param %s not found", e.ParamName).Error() -} - -type PathParamInvalidTypeError struct { - Err error - ParamName string - ParamValue string - ExpectedType string -} - -func (e PathParamInvalidTypeError) Error() string { - return fmt.Errorf("param %s=%s is not of type %s: %w", e.ParamName, e.ParamValue, e.ExpectedType, e.Err).Error() -} - func (c ginContext[B]) PathParamInt(name string) int { param, err := c.PathParamIntErr(name) if err != nil { From f21be7cbef4278066e383929f17129bed9cadeae Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Sun, 2 Feb 2025 20:34:10 +0800 Subject: [PATCH 07/11] Add tests --- ctx_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/ctx_test.go b/ctx_test.go index 8d971e1b..8d45c79c 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/xml" "errors" + "fmt" "net/http/httptest" "strings" "testing" @@ -27,6 +28,48 @@ func TestContext_PathParam(t *testing.T) { require.Equal(t, crlf(`{"ans":"123"}`), w.Body.String()) }) + t.Run("can read one path param to int", func(t *testing.T) { + s := NewServer() + Get(s, "/foo/{id}", func(c ContextNoBody) (ans, error) { + return ans{Ans: fmt.Sprintf("%d", c.PathParamInt("id"))}, nil + }) + + r := httptest.NewRequest("GET", "/foo/123", nil) + w := httptest.NewRecorder() + + s.Mux.ServeHTTP(w, r) + + require.Equal(t, crlf(`{"ans":"123"}`), w.Body.String()) + }) + + t.Run("reading non-int path param to int defaults to 0", func(t *testing.T) { + s := NewServer() + Get(s, "/foo/{id}", func(c ContextNoBody) (ans, error) { + return ans{Ans: fmt.Sprintf("%d", c.PathParamInt("id"))}, nil + }) + + r := httptest.NewRequest("GET", "/foo/abc", nil) + w := httptest.NewRecorder() + + s.Mux.ServeHTTP(w, r) + + require.Equal(t, crlf(`{"ans":"0"}`), w.Body.String()) + }) + + t.Run("reading missing path param to int defaults to 0", func(t *testing.T) { + s := NewServer() + Get(s, "/foo/", func(c ContextNoBody) (ans, error) { + return ans{Ans: fmt.Sprintf("%d", c.PathParamInt("id"))}, nil + }) + + r := httptest.NewRequest("GET", "/foo/", nil) + w := httptest.NewRecorder() + + s.Mux.ServeHTTP(w, r) + + require.Equal(t, crlf(`{"ans":"0"}`), w.Body.String()) + }) + t.Run("path param invalid", func(t *testing.T) { s := NewServer() Get(s, "/foo/", func(c ContextNoBody) (ans, error) { From 581dd0fc9a059a475cd7bee10b66298bd0092f07 Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Sun, 2 Feb 2025 20:34:24 +0800 Subject: [PATCH 08/11] Refactor logic --- ctx.go | 54 +++++++++++++++++++++++--------------- extra/fuegoecho/context.go | 25 ++---------------- extra/fuegogin/context.go | 25 ++---------------- 3 files changed, 37 insertions(+), 67 deletions(-) diff --git a/ctx.go b/ctx.go index 0c93d1d9..0faba099 100644 --- a/ctx.go +++ b/ctx.go @@ -223,7 +223,30 @@ func (c netHttpContext[B]) PathParam(name string) string { return c.Req.PathValue(name) } -func (c netHttpContext[B]) PathParamIntErr(name string) (int, error) { +type PathParamNotFoundError struct { + ParamName string +} + +func (e PathParamNotFoundError) Error() string { + return fmt.Errorf("param %s not found", e.ParamName).Error() +} + +type PathParamInvalidTypeError struct { + Err error + ParamName string + ParamValue string + ExpectedType string +} + +func (e PathParamInvalidTypeError) Error() string { + return fmt.Errorf("param %s=%s is not of type %s: %w", e.ParamName, e.ParamValue, e.ExpectedType, e.Err).Error() +} + +type ContextWithPathParam interface { + PathParam(name string) string +} + +func PathParamIntErr(c ContextWithPathParam, name string) (int, error) { param := c.PathParam(name) if param == "" { return 0, PathParamNotFoundError{ParamName: name} @@ -242,34 +265,23 @@ func (c netHttpContext[B]) PathParamIntErr(name string) (int, error) { return i, nil } -type PathParamNotFoundError struct { - ParamName string -} - -func (e PathParamNotFoundError) Error() string { - return fmt.Errorf("param %s not found", e.ParamName).Error() +func (c netHttpContext[B]) PathParamIntErr(name string) (int, error) { + return PathParamIntErr(c, name) } -type PathParamInvalidTypeError struct { - Err error - ParamName string - ParamValue string - ExpectedType string -} +func PathParamInt(c ContextWithPathParam, name string) (int) { + param, err := PathParamIntErr(c, name) + if err != nil { + return 0 + } -func (e PathParamInvalidTypeError) Error() string { - return fmt.Errorf("param %s=%s is not of type %s: %w", e.ParamName, e.ParamValue, e.ExpectedType, e.Err).Error() + return param } // PathParamInt returns the path parameter with the given name as an int. // If the query parameter does not exist, or if it is not an int, it returns 0. func (c netHttpContext[B]) PathParamInt(name string) int { - param, err := c.PathParamIntErr(name) - if err != nil { - return 0 - } - - return param + return PathParamInt(c, name) } func (c netHttpContext[B]) MainLang() string { diff --git a/extra/fuegoecho/context.go b/extra/fuegoecho/context.go index ce73f405..ddf5642c 100644 --- a/extra/fuegoecho/context.go +++ b/extra/fuegoecho/context.go @@ -4,7 +4,6 @@ import ( "context" "errors" "net/http" - "strconv" "strings" "github.com/go-fuego/fuego" @@ -57,31 +56,11 @@ func (c echoContext[B]) PathParam(name string) string { } func (c echoContext[B]) PathParamIntErr(name string) (int, error) { - param := c.PathParam(name) - if param == "" { - return 0, fuego.PathParamNotFoundError{ParamName: name} - } - - i, err := strconv.Atoi(param) - if err != nil { - return 0, fuego.PathParamInvalidTypeError{ - ParamName: name, - ParamValue: param, - ExpectedType: "int", - Err: err, - } - } - - return i, nil + return fuego.PathParamIntErr(c, name) } func (c echoContext[B]) PathParamInt(name string) int { - param, err := c.PathParamIntErr(name) - if err != nil { - return 0 - } - - return param + return fuego.PathParamInt(c, name) } func (c echoContext[B]) MainLang() string { diff --git a/extra/fuegogin/context.go b/extra/fuegogin/context.go index e8463101..ba308798 100644 --- a/extra/fuegogin/context.go +++ b/extra/fuegogin/context.go @@ -4,7 +4,6 @@ import ( "context" "errors" "net/http" - "strconv" "strings" "github.com/gin-gonic/gin" @@ -57,31 +56,11 @@ func (c ginContext[B]) PathParam(name string) string { } func (c ginContext[B]) PathParamIntErr(name string) (int, error) { - param := c.PathParam(name) - if param == "" { - return 0, fuego.PathParamNotFoundError{ParamName: name} - } - - i, err := strconv.Atoi(param) - if err != nil { - return 0, fuego.PathParamInvalidTypeError{ - ParamName: name, - ParamValue: param, - ExpectedType: "int", - Err: err, - } - } - - return i, nil + return fuego.PathParamIntErr(c, name) } func (c ginContext[B]) PathParamInt(name string) int { - param, err := c.PathParamIntErr(name) - if err != nil { - return 0 - } - - return param + return fuego.PathParamInt(c, name) } func (c ginContext[B]) MainLang() string { From 0aa5414f11a155f0d38f38edf5e13e8ad48f40d6 Mon Sep 17 00:00:00 2001 From: Beiming Zhang Date: Mon, 3 Feb 2025 02:08:58 +0800 Subject: [PATCH 09/11] Format --- extra/fuegoecho/context.go | 4 ++-- extra/fuegogin/context.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extra/fuegoecho/context.go b/extra/fuegoecho/context.go index ddf5642c..87c8500e 100644 --- a/extra/fuegoecho/context.go +++ b/extra/fuegoecho/context.go @@ -56,11 +56,11 @@ func (c echoContext[B]) PathParam(name string) string { } func (c echoContext[B]) PathParamIntErr(name string) (int, error) { - return fuego.PathParamIntErr(c, name) + return fuego.PathParamIntErr(c, name) } func (c echoContext[B]) PathParamInt(name string) int { - return fuego.PathParamInt(c, name) + return fuego.PathParamInt(c, name) } func (c echoContext[B]) MainLang() string { diff --git a/extra/fuegogin/context.go b/extra/fuegogin/context.go index ba308798..2f9d1b7f 100644 --- a/extra/fuegogin/context.go +++ b/extra/fuegogin/context.go @@ -56,11 +56,11 @@ func (c ginContext[B]) PathParam(name string) string { } func (c ginContext[B]) PathParamIntErr(name string) (int, error) { - return fuego.PathParamIntErr(c, name) + return fuego.PathParamIntErr(c, name) } func (c ginContext[B]) PathParamInt(name string) int { - return fuego.PathParamInt(c, name) + return fuego.PathParamInt(c, name) } func (c ginContext[B]) MainLang() string { From f19c915efc40eb9333fe7dc45686d6dca1e09ea9 Mon Sep 17 00:00:00 2001 From: EwenQuim Date: Mon, 3 Feb 2025 11:45:53 +0100 Subject: [PATCH 10/11] Adds test and status code to PathParamIntErr --- ctx.go | 10 +++++++--- ctx_test.go | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/ctx.go b/ctx.go index 0faba099..84c9f824 100644 --- a/ctx.go +++ b/ctx.go @@ -231,6 +231,8 @@ func (e PathParamNotFoundError) Error() string { return fmt.Errorf("param %s not found", e.ParamName).Error() } +func (e PathParamNotFoundError) StatusCode() int { return 404 } + type PathParamInvalidTypeError struct { Err error ParamName string @@ -242,6 +244,8 @@ func (e PathParamInvalidTypeError) Error() string { return fmt.Errorf("param %s=%s is not of type %s: %w", e.ParamName, e.ParamValue, e.ExpectedType, e.Err).Error() } +func (e PathParamInvalidTypeError) StatusCode() int { return 422 } + type ContextWithPathParam interface { PathParam(name string) string } @@ -266,10 +270,10 @@ func PathParamIntErr(c ContextWithPathParam, name string) (int, error) { } func (c netHttpContext[B]) PathParamIntErr(name string) (int, error) { - return PathParamIntErr(c, name) + return PathParamIntErr(c, name) } -func PathParamInt(c ContextWithPathParam, name string) (int) { +func PathParamInt(c ContextWithPathParam, name string) int { param, err := PathParamIntErr(c, name) if err != nil { return 0 @@ -281,7 +285,7 @@ func PathParamInt(c ContextWithPathParam, name string) (int) { // PathParamInt returns the path parameter with the given name as an int. // If the query parameter does not exist, or if it is not an int, it returns 0. func (c netHttpContext[B]) PathParamInt(name string) int { - return PathParamInt(c, name) + return PathParamInt(c, name) } func (c netHttpContext[B]) MainLang() string { diff --git a/ctx_test.go b/ctx_test.go index 8d45c79c..e9bf1070 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -70,6 +70,24 @@ func TestContext_PathParam(t *testing.T) { require.Equal(t, crlf(`{"ans":"0"}`), w.Body.String()) }) + t.Run("reading non-int path param to int sends an error", func(t *testing.T) { + s := NewServer() + Get(s, "/foo/{id}", func(c ContextNoBody) (ans, error) { + id, err := c.PathParamIntErr("id") + if err != nil { + return ans{}, err + } + return ans{Ans: fmt.Sprintf("%d", id)}, nil + }) + + r := httptest.NewRequest("GET", "/foo/abc", nil) + w := httptest.NewRecorder() + + s.Mux.ServeHTTP(w, r) + + require.Equal(t, 422, w.Code) + }) + t.Run("path param invalid", func(t *testing.T) { s := NewServer() Get(s, "/foo/", func(c ContextNoBody) (ans, error) { From bd97a3a40b081eb94b59fde74b7ba37d2dbd6e97 Mon Sep 17 00:00:00 2001 From: EwenQuim Date: Mon, 3 Feb 2025 11:46:38 +0100 Subject: [PATCH 11/11] Moved comment to above the field --- ctx.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index 84c9f824..45743590 100644 --- a/ctx.go +++ b/ctx.go @@ -45,7 +45,8 @@ type ContextWithBody[B any] interface { // ... // }) PathParam(name string) string - PathParamInt(name string) int // If the path parameter is not provided or is not an int, it returns 0. Use [Ctx.PathParamIntErr] if you want to know if the path parameter is erroneous. + // If the path parameter is not provided or is not an int, it returns 0. Use [Ctx.PathParamIntErr] if you want to know if the path parameter is erroneous. + PathParamInt(name string) int PathParamIntErr(name string) (int, error) QueryParam(name string) string