fix(web): handle TypeCastError when building form validation messages (#40212)

Fixes https://github.com/go-gitea/gitea/issues/40210

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Alan Hsu
2026-11-06 23:27:06 +00:00
committed by GitHub
parent ede9ec3e10
commit df4544a58b
3 changed files with 28 additions and 9 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -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)
})
}