Reworked Required and OmitEmpty #8

Merged
lunny merged 4 commits from KN4CK3R/binding:feature-ignore-empty into master 2021-06-09 15:32:02 +00:00
3 changed files with 141 additions and 38 deletions

View File

@ -388,32 +388,41 @@ func validateField(errors Errors, zero interface{}, field reflect.StructField, f
}
}
rules := strings.Split(field.Tag.Get("binding"), ";")
if reflect.DeepEqual(zero, fieldValue) {
for _, rule := range rules {
if rule == "Required" {
errors.Add([]string{field.Name}, ERR_REQUIRED, "Required")
break
}
if strings.HasPrefix(rule, "Default(") {
if fieldVal.CanSet() {
errors = setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors)
} else {
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default")
}
break
}
}
return errors
}
VALIDATE_RULES:
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
for _, rule := range rules {
if len(rule) == 0 {
continue
}
switch {
case rule == "OmitEmpty":
if reflect.DeepEqual(zero, fieldValue) {
break VALIDATE_RULES
}
case rule == "Required":
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice {
if v.Len() == 0 {
errors.Add([]string{field.Name}, ERR_REQUIRED, "Required")
break VALIDATE_RULES
}
continue
case strings.HasPrefix(rule, "Default("):
continue
case rule == "OmitEmpty": // legacy
continue
continue
}
if reflect.DeepEqual(zero, fieldValue) {
errors.Add([]string{field.Name}, ERR_REQUIRED, "Required")
break VALIDATE_RULES
}
case rule == "AlphaDash":
if AlphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_ALPHA_DASH, "AlphaDash")
@ -430,8 +439,7 @@ VALIDATE_RULES:
errors.Add([]string{field.Name}, ERR_SIZE, "Size")
break VALIDATE_RULES
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() != size {
if fieldVal.Kind() == reflect.Slice && fieldVal.Len() != size {
errors.Add([]string{field.Name}, ERR_SIZE, "Size")
break VALIDATE_RULES
}
@ -441,8 +449,7 @@ VALIDATE_RULES:
errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize")
break VALIDATE_RULES
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() < min {
if fieldVal.Kind() == reflect.Slice && fieldVal.Len() < min {
errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize")
break VALIDATE_RULES
}
@ -452,8 +459,7 @@ VALIDATE_RULES:
errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize")
break VALIDATE_RULES
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() > max {
if fieldVal.Kind() == reflect.Slice && fieldVal.Len() > max {
errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize")
break VALIDATE_RULES
}
@ -474,9 +480,7 @@ VALIDATE_RULES:
}
case rule == "Url":
str := fmt.Sprintf("%v", fieldValue)
if len(str) == 0 {
continue
} else if !isURL(str) {
if !isURL(str) {
errors.Add([]string{field.Name}, ERR_URL, "Url")
break VALIDATE_RULES
}
@ -500,15 +504,6 @@ VALIDATE_RULES:
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Exclude")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Default("):
if reflect.DeepEqual(zero, fieldValue) {
if fieldVal.CanAddr() {
errors = setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors)
} else {
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default")
break VALIDATE_RULES
}
}
default:
// Apply custom validation rules
var isValid bool

View File

@ -74,7 +74,7 @@ type (
NotIn string `form:"NotIn" binding:"NotIn(1,2,3)"`
Include string `form:"Include" binding:"Include(a)"`
Exclude string `form:"Exclude" binding:"Exclude(a)"`
Empty string `binding:"OmitEmpty"`
Empty string
}
Group struct {

View File

@ -401,6 +401,114 @@ var validationTestCases = []validationTestCase{
},
},
},
{
description: "no errors with not required fields",
data: []*struct {
AlphaDash string `binding:"AlphaDash"`
AlphaDashDot string `binding:"AlphaDashDot"`
Size string `binding:"Size(1)"`
SizeSlice []string `binding:"Size(1)"`
MinSize string `binding:"MinSize(5)"`
MinSizeSlice []string `binding:"MinSize(5)"`
MaxSize string `binding:"MaxSize(1)"`
MaxSizeSlice []string `binding:"MaxSize(1)"`
Range int `binding:"Range(1,2)"`
Email string `binding:"Email"`
Url string `binding:"Url"`
In string `binding:"Default(0);In(1,2,3)"`
NotIn string `binding:"NotIn(1,2,3)"`
} {
{},
},
expectedErrors: Errors{},
},
{
description: "errors with required fields",
data: []*struct {
AlphaDash string `binding:"Required;AlphaDash"`
AlphaDashDot string `binding:"Required;AlphaDashDot"`
Size string `binding:"Required;Size(1)"`
SizeSlice []string `binding:"Required;Size(1)"`
MinSize string `binding:"Required;MinSize(5)"`
MinSizeSlice []string `binding:"Required;MinSize(5)"`
MaxSize string `binding:"Required;MaxSize(1)"`
MaxSizeSlice []string `binding:"Required;MaxSize(1)"`
Range int `binding:"Required;Range(1,2)"`
Email string `binding:"Required;Email"`
Url string `binding:"Required;Url"`
In string `binding:"Required;Default(0);In(1,2,3)"`
NotIn string `binding:"Required;NotIn(1,2,3)"`
} {
{},
},
expectedErrors: Errors{
Error{
FieldNames: []string{"AlphaDash"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"AlphaDashDot"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"Size"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"SizeSlice"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"MinSize"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"MinSizeSlice"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"MaxSize"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"MaxSizeSlice"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"Range"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"Email"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"Url"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"In"},
Classification: "Required",
Message: "Required",
},
Error{
FieldNames: []string{"NotIn"},
Classification: "Required",
Message: "Required",
},
},
},
}
func Test_Validation(t *testing.T) {
@ -415,7 +523,7 @@ func performValidationTest(t *testing.T, testCase validationTestCase) {
m.Post(testRoute, func(resp http.ResponseWriter, req *http.Request) {
actual := Validate(req, testCase.data)
assert.EqualValues(t, fmt.Sprintf("%+v", testCase.expectedErrors), fmt.Sprintf("%+v", actual))
assert.EqualValues(t, fmt.Sprintf("%+v", testCase.expectedErrors), fmt.Sprintf("%+v", actual), testCase.description)
})
req, err := http.NewRequest("POST", testRoute, nil)