From df4544a58b0e588f46bbb7422eb0dde7de031466 Mon Sep 17 00:00:00 2001 From: Alan Hsu Date: Fri, 6 Nov 2026 23:27:06 +0000 Subject: [PATCH] fix(web): handle TypeCastError when building form validation messages (#40212) Fixes https://github.com/go-gitea/gitea/issues/40210 --------- Co-authored-by: wxiaoguang --- modules/web/binding/binding.go | 5 ++++- modules/web/middleware/binding.go | 2 ++ modules/web/middleware/binding_test.go | 30 +++++++++++++++++++------- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/modules/web/binding/binding.go b/modules/web/binding/binding.go index fb243092265..e2c044960e9 100644 --- a/modules/web/binding/binding.go +++ b/modules/web/binding/binding.go @@ -28,10 +28,13 @@ import ( ) const ( + // TODO: need to refactor the binding package, let the rules provide error message directly. + // Don't use the fragile Classification and useless&incorrect Message field in the future. + errContentType = "ContentTypeError" errDeserialization = "DeserializationError" errTypeCast = "TypeCastError" - errRule = "RuleError" + ErrTypeCast = errTypeCast ErrRequired = "RequiredError" ErrAlphaDashDot = "AlphaDashDotError" diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go index 346cc1cf3f7..71ff40aa37b 100644 --- a/modules/web/middleware/binding.go +++ b/modules/web/middleware/binding.go @@ -174,6 +174,8 @@ func BuildValidationErrorForUser(f any, l translation.Locale, bindingErrs valida errorMessage = l.TrString("form.invalid_group_team_map_error", fieldDisplayName, bindingErrMsg) case validation.ErrInvalidBadgeSlug: errorMessage = l.TrString("form.invalid_slug_error", fieldDisplayName) + case binding.ErrTypeCast: + errorMessage = l.TrString("form.field_invalid_message", fieldDisplayName, bindingErrMsg) default: setting.PanicInDevOrTesting("unknown binding error classification for field %T.%s: %v, err: %s", f, errorFieldName, classification, bindingErrMsg) var msg string diff --git a/modules/web/middleware/binding_test.go b/modules/web/middleware/binding_test.go index 0cd8dd6c79c..4ad477fabf9 100644 --- a/modules/web/middleware/binding_test.go +++ b/modules/web/middleware/binding_test.go @@ -4,6 +4,9 @@ package middleware import ( + "net/http" + "net/http/httptest" + "strings" "testing" "gitea.dev/modules/translation" @@ -12,17 +15,28 @@ import ( "github.com/stretchr/testify/assert" ) -type testRangeForm struct { +type testBindingForm struct { FormDefaultValidator Hours int `binding:"Range(0,1000)"` } func TestBuildValidationErrorForUser(t *testing.T) { - // an out-of-range value must reach its own message instead of the panicking "default" branch - form := &testRangeForm{Hours: 2000} - errs := validation.Binder().Validate(t.Context(), form) - errorMessage, errorFieldName, fieldNames := BuildValidationErrorForUser(form, translation.MockLocale{}, errs) - assert.Equal(t, "form.range_error:form.Hours,0,1000", errorMessage) - assert.Equal(t, "Hours", errorFieldName) - assert.Equal(t, []string{"Hours"}, fieldNames) + t.Run("Range", func(t *testing.T) { + form := &testBindingForm{Hours: 2000} + errs := validation.Binder().Validate(t.Context(), form) + errorMessage, errorFieldName, fieldNames := BuildValidationErrorForUser(form, translation.MockLocale{}, errs) + assert.Equal(t, "form.range_error:form.Hours,0,1000", errorMessage) + assert.Equal(t, "Hours", errorFieldName) + assert.Equal(t, []string{"Hours"}, fieldNames) + }) + t.Run("TypeCast", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("hours=abc")) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + form := &testBindingForm{} + errs := validation.Binder().Bind(req, form) + errorMessage, errorFieldName, fieldNames := BuildValidationErrorForUser(form, translation.MockLocale{}, errs) + assert.Equal(t, "form.field_invalid_message:form.Hours,Value could not be parsed as integer", errorMessage) + assert.Equal(t, "Hours", errorFieldName) + assert.Equal(t, []string{"Hours"}, fieldNames) + }) }