mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-15 04:57:07 +08:00
282 lines
7.0 KiB
Go
282 lines
7.0 KiB
Go
package binding
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func Validate(obj interface{}) error {
|
|
|
|
typ := reflect.TypeOf(obj)
|
|
value := reflect.ValueOf(obj)
|
|
|
|
// Check to ensure we are getting a valid
|
|
// pointer for manipulation.
|
|
if typ.Kind() == reflect.Ptr {
|
|
typ = typ.Elem()
|
|
value = value.Elem()
|
|
}
|
|
|
|
// Kill process if obj did not pass in a scruct.
|
|
// This happens when a pointer passed in.
|
|
if value.Kind() != reflect.Struct {
|
|
return nil
|
|
}
|
|
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
|
|
field := typ.Field(i)
|
|
fieldValue := value.Field(i).Interface()
|
|
zero := reflect.Zero(field.Type).Interface()
|
|
|
|
// Validate nested and embedded structs (if pointer, only do so if not nil)
|
|
if field.Type.Kind() == reflect.Struct ||
|
|
(field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) {
|
|
if err := Validate(fieldValue); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if field.Tag.Get("validate") != "" || field.Tag.Get("binding") != "" {
|
|
// Break validate field into array
|
|
array := strings.Split(field.Tag.Get("validate"), "|")
|
|
|
|
// Legacy Support for binding.
|
|
if array[0] == "" {
|
|
array = strings.Split(field.Tag.Get("binding"), "|")
|
|
}
|
|
|
|
// Do the hard work of checking all assertions
|
|
for setting := range array {
|
|
|
|
match := array[setting]
|
|
|
|
//Check that value was passed in and is not required.
|
|
if match != "required" && null(fieldValue) == true {
|
|
return nil
|
|
}
|
|
|
|
switch {
|
|
case "required" == match:
|
|
if err := required(field, fieldValue, zero); err != nil {
|
|
return err
|
|
}
|
|
case "email" == match:
|
|
if err := regex(`regex:^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$`, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
case strings.HasPrefix(match, "min:"):
|
|
if err := min(match, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
case strings.HasPrefix(match, "max:"):
|
|
if err := max(match, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
case strings.HasPrefix(match, "in:"):
|
|
if err := in(match, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
case strings.HasPrefix(match, "regex:"):
|
|
if err := regex(match, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
case strings.HasPrefix(match, "length:"):
|
|
if err := length(match, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
case strings.HasPrefix(match, "length_between:"):
|
|
if err := length_between(match, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
panic("The field " + match + " is not a valid validation check.")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Ensure that the value being passed in is not of type nil.
|
|
func null(value interface{}) bool {
|
|
if reflect.ValueOf(value).IsNil() {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Check that the following function features
|
|
// the required field. May need to check for
|
|
// more special cases like since passing in null
|
|
// is the same as 0 for int type checking.
|
|
func required(field reflect.StructField, value, zero interface{}) error {
|
|
|
|
if reflect.DeepEqual(zero, value) {
|
|
if _, ok := value.(int); !ok {
|
|
return errors.New("The required field " + field.Name + " was not submitted.")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Check that the passed in field is a valid email
|
|
// Need to improve error logging for this method
|
|
// Currently only supports strings, ints
|
|
func in(field string, value interface{}) error {
|
|
|
|
if data, ok := value.(*string); ok {
|
|
if len(*data) == 0 {
|
|
return nil
|
|
}
|
|
|
|
valid := strings.Split(field[3:], ",")
|
|
|
|
for option := range valid {
|
|
if valid[option] == *data {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
} else {
|
|
return errors.New("The value passed in for IN could not be converted to a string.")
|
|
}
|
|
|
|
return errors.New("In did not match any of the expected values.")
|
|
}
|
|
|
|
func min(field string, value interface{}) error {
|
|
|
|
if data, ok := value.(*int); ok {
|
|
|
|
min := field[strings.Index(field, ":")+1:]
|
|
|
|
if minNum, ok := strconv.ParseInt(min, 0, 64); ok == nil {
|
|
|
|
if int64(*data) >= minNum {
|
|
return nil
|
|
} else {
|
|
return errors.New("The data you passed in was smaller then the allowed minimum.")
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return errors.New("The value passed in for MIN could not be converted to an int.")
|
|
}
|
|
|
|
func max(field string, value interface{}) error {
|
|
|
|
if data, ok := value.(*int); ok {
|
|
|
|
max := field[strings.Index(field, ":")+1:]
|
|
|
|
if maxNum, ok := strconv.ParseInt(max, 0, 64); ok == nil {
|
|
if int64(*data) <= maxNum {
|
|
return nil
|
|
} else {
|
|
return errors.New("The data you passed in was larger than the maximum.")
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return errors.New("The value passed in for MAX could not be converted to an int.")
|
|
}
|
|
|
|
// Regex handles the general regex call and also handles
|
|
// the regex email.
|
|
func regex(field string, value interface{}) error {
|
|
|
|
reg := field[strings.Index(field, ":")+1:]
|
|
|
|
if data, ok := value.(*string); ok {
|
|
if len(*data) == 0 {
|
|
return nil
|
|
} else if err := match_regex(reg, []byte(*data)); err != nil {
|
|
return err
|
|
}
|
|
} else if data, ok := value.(*int); ok {
|
|
if err := match_regex(reg, []byte(strconv.Itoa(*data))); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return errors.New("The value passed in for REGEX could not be converted to a string or int.")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Helper function for regex.
|
|
func match_regex(reg string, data []byte) error {
|
|
|
|
if match, err := regexp.Match(reg, []byte(data)); err == nil && match {
|
|
return nil
|
|
} else {
|
|
return errors.New("Your regex did not match or was not valid.")
|
|
}
|
|
}
|
|
|
|
// Check passed in json length string is exact value passed in.
|
|
// Also checks if passed in values is between two different ones.
|
|
func length(field string, value interface{}) error {
|
|
|
|
length := field[strings.Index(field, ":")+1:]
|
|
|
|
if data, ok := value.(*string); ok {
|
|
if intdata, intok := strconv.Atoi(length); intok == nil {
|
|
if len(*data) == intdata {
|
|
return nil
|
|
} else {
|
|
return errors.New("The data passed in was not equal to the expected length.")
|
|
}
|
|
} else {
|
|
return errors.New("The value passed in for LENGTH could not be converted to an int.")
|
|
}
|
|
} else {
|
|
return errors.New("The value passed in for LENGTH could not be converted to a string.")
|
|
}
|
|
}
|
|
|
|
// Check if the strings length is between high,low.
|
|
func length_between(field string, value interface{}) error {
|
|
|
|
length := field[strings.Index(field, ":")+1:]
|
|
vals := strings.Split(length, ",")
|
|
|
|
if len(vals) == 2 {
|
|
|
|
if data, ok := value.(*string); ok {
|
|
|
|
if lowerbound, lowok := strconv.Atoi(vals[0]); lowok == nil {
|
|
|
|
if upperbound, upok := strconv.Atoi(vals[1]); upok == nil {
|
|
|
|
if lowerbound <= len(*data) && upperbound >= len(*data) {
|
|
return nil
|
|
} else {
|
|
return errors.New("The value passed in for LENGTH BETWEEN was not in bounds.")
|
|
}
|
|
|
|
} else {
|
|
return errors.New("The value passed in for LENGTH BETWEEN could not be converted to an int.")
|
|
}
|
|
|
|
} else {
|
|
return errors.New("The value passed in for LENGTH BETWEEN could not be converted to an int.")
|
|
}
|
|
|
|
} else {
|
|
return errors.New("The value passed in for LENGTH BETWEEN could not be converted to a string.")
|
|
}
|
|
} else {
|
|
return errors.New("LENGTH BETWEEN requires exactly two paramaters.")
|
|
}
|
|
}
|