diff --git a/validation.go b/validation.go index bb06c7f46..1aeb5bb9c 100644 --- a/validation.go +++ b/validation.go @@ -6,6 +6,7 @@ import ( "net/url" "regexp" "runtime" + "unicode" ) // Simple struct to store the Message & Key of a validation error @@ -153,7 +154,7 @@ func (v *Validation) apply(chk Validator, obj interface{}) *ValidationResult { // Add the error to the validation context. err := &ValidationError{ - Message: chk.DefaultMessage(), + Message: fmt.Sprintf("%s %s", formatFieldName(key), chk.DefaultMessage()), Key: key, } v.Errors = append(v.Errors, err) @@ -243,6 +244,24 @@ func restoreValidationErrors(req *http.Request) ([]*ValidationError, error) { return errors, err } +// Format field name for display +func formatFieldName(field string) (formattedField string) { + if field == "" { + formattedField = "Field" + } else { + var words []rune + for _, character := range field { + if unicode.IsUpper(character) { + words = append(words, rune(' ')) + } + words = append(words, unicode.ToLower(rune(character))) + } + words[0] = unicode.ToUpper(words[0]) + formattedField = string(words) + } + return +} + // Register default validation keys for all calls to Controller.Validation.Func(). // Map from (package).func => (line => name of first arg to Validation func) // E.g. "myapp/controllers.helper" or "myapp/controllers.(*Application).Action" diff --git a/validation_test.go b/validation_test.go index 5f2ff7928..ed94d7470 100644 --- a/validation_test.go +++ b/validation_test.go @@ -74,3 +74,17 @@ func TestValidationNoKeepCookiePreviouslySet(t *testing.T) { t.Fatalf("cookie should be deleted") } } + +// Test that field names can be properly formatted for validation error messages +func TestFieldNameFormatting(t *testing.T) { + fieldAscii := "someField" + expectedAscii := "Some field" + fieldUnicode := "sömeϜield" + expectedUnicode := "Söme ϝield" + if formattedAscii := formatFieldName(fieldAscii); formattedAscii != expectedAscii { + t.Errorf("ASCII field formatting failed. Expecting %s, got %s", expectedAscii, formattedAscii) + } + if formattedUnicode := formatFieldName(fieldUnicode); formattedUnicode != expectedUnicode { + t.Errorf("ASCII field formatting failed. Expecting %s, got %s", expectedUnicode, formattedUnicode) + } +} diff --git a/validators.go b/validators.go index 27182c2c7..407e517d1 100644 --- a/validators.go +++ b/validators.go @@ -39,7 +39,7 @@ func (r Required) IsSatisfied(obj interface{}) bool { } func (r Required) DefaultMessage() string { - return "Required" + return "is required" } type Min struct { @@ -55,7 +55,7 @@ func (m Min) IsSatisfied(obj interface{}) bool { } func (m Min) DefaultMessage() string { - return fmt.Sprintln("Minimum is", m.Min) + return fmt.Sprintln("minimum is", m.Min) } type Max struct { @@ -71,7 +71,7 @@ func (m Max) IsSatisfied(obj interface{}) bool { } func (m Max) DefaultMessage() string { - return fmt.Sprintln("Maximum is", m.Max) + return fmt.Sprintln("maximum is", m.Max) } // Requires an integer to be within Min, Max inclusive. @@ -85,7 +85,7 @@ func (r Range) IsSatisfied(obj interface{}) bool { } func (r Range) DefaultMessage() string { - return fmt.Sprintln("Range is", r.Min.Min, "to", r.Max.Max) + return fmt.Sprintln("range is", r.Min.Min, "to", r.Max.Max) } // Requires an array or string to be at least a given length. @@ -105,7 +105,7 @@ func (m MinSize) IsSatisfied(obj interface{}) bool { } func (m MinSize) DefaultMessage() string { - return fmt.Sprintln("Minimum size is", m.Min) + return fmt.Sprintln("minimum size is", m.Min) } // Requires an array or string to be at most a given length. @@ -125,7 +125,7 @@ func (m MaxSize) IsSatisfied(obj interface{}) bool { } func (m MaxSize) DefaultMessage() string { - return fmt.Sprintln("Maximum size is", m.Max) + return fmt.Sprintln("maximum size is", m.Max) } // Requires an array or string to be exactly a given length. @@ -145,7 +145,7 @@ func (s Length) IsSatisfied(obj interface{}) bool { } func (s Length) DefaultMessage() string { - return fmt.Sprintln("Required length is", s.N) + return fmt.Sprintln("required length is", s.N) } // Requires a string to match a given regex. @@ -159,7 +159,7 @@ func (m Match) IsSatisfied(obj interface{}) bool { } func (m Match) DefaultMessage() string { - return fmt.Sprintln("Must match", m.Regexp) + return fmt.Sprintln("must match", m.Regexp) } var emailPattern = regexp.MustCompile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?") @@ -169,5 +169,5 @@ type Email struct { } func (e Email) DefaultMessage() string { - return fmt.Sprintln("Must be a valid email address") + return fmt.Sprintln("must be a valid email address") }