github.com/gogf/gf@v1.16.9/util/gvalid/gvalid.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  // Package gvalid implements powerful and useful data/form validation functionality.
     8  package gvalid
     9  
    10  import (
    11  	"context"
    12  	"regexp"
    13  	"strings"
    14  
    15  	"github.com/gogf/gf/text/gregex"
    16  )
    17  
    18  // Refer to Laravel validation:
    19  // https://laravel.com/docs/5.5/validation#available-validation-rules
    20  // https://learnku.com/docs/laravel/5.4/validation
    21  //
    22  // All supported rules:
    23  // required             format: required                              brief: Required.
    24  // required-if          format: required-if:field,value,...           brief: Required unless all given field and its value are equal.
    25  // required-unless      format: required-unless:field,value,...       brief: Required unless all given field and its value are not equal.
    26  // required-with        format: required-with:field1,field2,...       brief: Required if any of given fields are not empty.
    27  // required-with-all    format: required-with-all:field1,field2,...   brief: Required if all given fields are not empty.
    28  // required-without     format: required-without:field1,field2,...    brief: Required if any of given fields are empty.
    29  // required-without-all format: required-without-all:field1,field2,...brief: Required if all given fields are empty.
    30  // bail                 format: bail                                  brief: Stop validating when this field's validation failed.
    31  // date                 format: date                                  brief: Standard date, like: 2006-01-02, 20060102, 2006.01.02
    32  // date-format          format: date-format:format                    brief: Custom date format.
    33  // email                format: email                                 brief: Email address.
    34  // phone                format: phone                                 brief: Phone number.
    35  // telephone            format: telephone                             brief: Telephone number, like: "XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
    36  // passport             format: passport                              brief: Universal passport format rule: Starting with letter, containing only numbers or underscores, length between 6 and 18
    37  // password             format: password                              brief: Universal password format rule1: Containing any visible chars, length between 6 and 18.
    38  // password2            format: password2                             brief: Universal password format rule2: Must meet password rule1, must contain lower and upper letters and numbers.
    39  // password3            format: password3                             brief: Universal password format rule3: Must meet password rule1, must contain lower and upper letters, numbers and special chars.
    40  // postcode             format: postcode                              brief: Postcode number.
    41  // resident-id          format: resident-id                           brief: Resident id number.
    42  // bank-card            format: bank-card                             brief: Bank card nunber.
    43  // qq                   format: qq                                    brief: Tencent QQ number.
    44  // ip                   format: ip                                    brief: IPv4/IPv6.
    45  // ipv4                 format: ipv4                                  brief: IPv4.
    46  // ipv6                 format: ipv6                                  brief: IPv6.
    47  // mac                  format: mac                                   brief: MAC.
    48  // url                  format: url                                   brief: URL.
    49  // domain               format: domain                                brief: Domain.
    50  // length               format: length:min,max                        brief: Length between :min and :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
    51  // min-length           format: min-length:min                        brief: Length is equal or greater than :min. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
    52  // max-length           format: max-length:max                        brief: Length is equal or lesser than :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
    53  // size                 format: size:size							  brief: Length must be :size. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
    54  // between              format: between:min,max                       brief: Range between :min and :max. It supports both integer and float.
    55  // min                  format: min:min                               brief: Equal or greater than :min. It supports both integer and float.
    56  // max                  format: max:max                               brief: Equal or lesser than :max. It supports both integer and float.
    57  // json                 format: json                                  brief: JSON.
    58  // integer              format: integer                               brief: Integer.
    59  // float                format: float                                 brief: Float. Note that an integer is actually a float number.
    60  // boolean              format: boolean                               brief: Boolean(1,true,on,yes:true | 0,false,off,no,"":false)
    61  // same                 format: same:field                            brief: Value should be the same as value of field.
    62  // different            format: different:field                       brief: Value should be different from value of field.
    63  // in                   format: in:value1,value2,...                  brief: Value should be in: value1,value2,...
    64  // not-in               format: not-in:value1,value2,...              brief: Value should not be in: value1,value2,...
    65  // regex                format: regex:pattern                         brief: Value should match custom regular expression pattern.
    66  
    67  // CustomMsg is the custom error message type,
    68  // like: map[field] => string|map[rule]string
    69  type CustomMsg = map[string]interface{}
    70  
    71  // fieldRule defined the alias name and rule string for specified field.
    72  type fieldRule struct {
    73  	Name string // Alias name for the field.
    74  	Rule string // Rule string like: "max:6"
    75  }
    76  
    77  // apiNoValidation is an interface that marks current struct not validated by package `gvalid`.
    78  type apiNoValidation interface {
    79  	NoValidation()
    80  }
    81  
    82  const (
    83  	singleRulePattern         = `^([\w-]+):{0,1}(.*)` // regular expression pattern for single validation rule.
    84  	internalRulesErrRuleName  = "InvalidRules"        // rule name for internal invalid rules validation error.
    85  	internalParamsErrRuleName = "InvalidParams"       // rule name for internal invalid params validation error.
    86  	internalObjectErrRuleName = "InvalidObject"       // rule name for internal invalid object validation error.
    87  	internalErrorMapKey       = "__InternalError__"   // error map key for internal errors.
    88  	internalDefaultRuleName   = "__default__"         // default rule name for i18n error message format if no i18n message found for specified error rule.
    89  	ruleMessagePrefixForI18n  = "gf.gvalid.rule."     // prefix string for each rule configuration in i18n content.
    90  	noValidationTagName       = "nv"                  // no validation tag name for struct attribute.
    91  	bailRuleName              = "bail"                // the name for rule "bail"
    92  )
    93  
    94  var (
    95  	defaultValidator     = New()                            // defaultValidator is the default validator for package functions.
    96  	structTagPriority    = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array.
    97  	aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array.
    98  
    99  	// all internal error keys.
   100  	internalErrKeyMap = map[string]string{
   101  		internalRulesErrRuleName:  internalRulesErrRuleName,
   102  		internalParamsErrRuleName: internalParamsErrRuleName,
   103  		internalObjectErrRuleName: internalObjectErrRuleName,
   104  	}
   105  	// regular expression object for single rule
   106  	// which is compiled just once and of repeatable usage.
   107  	ruleRegex, _ = regexp.Compile(singleRulePattern)
   108  
   109  	// mustCheckRulesEvenValueEmpty specifies some rules that must be validated
   110  	// even the value is empty (nil or empty).
   111  	mustCheckRulesEvenValueEmpty = map[string]struct{}{
   112  		"required":             {},
   113  		"required-if":          {},
   114  		"required-unless":      {},
   115  		"required-with":        {},
   116  		"required-with-all":    {},
   117  		"required-without":     {},
   118  		"required-without-all": {},
   119  		//"same":                 {},
   120  		//"different":            {},
   121  		//"in":                   {},
   122  		//"not-in":               {},
   123  		//"regex":                {},
   124  	}
   125  	// allSupportedRules defines all supported rules that is used for quick checks.
   126  	allSupportedRules = map[string]struct{}{
   127  		"required":             {},
   128  		"required-if":          {},
   129  		"required-unless":      {},
   130  		"required-with":        {},
   131  		"required-with-all":    {},
   132  		"required-without":     {},
   133  		"required-without-all": {},
   134  		"bail":                 {},
   135  		"date":                 {},
   136  		"date-format":          {},
   137  		"email":                {},
   138  		"phone":                {},
   139  		"phone-loose":          {},
   140  		"telephone":            {},
   141  		"passport":             {},
   142  		"password":             {},
   143  		"password2":            {},
   144  		"password3":            {},
   145  		"postcode":             {},
   146  		"resident-id":          {},
   147  		"bank-card":            {},
   148  		"qq":                   {},
   149  		"ip":                   {},
   150  		"ipv4":                 {},
   151  		"ipv6":                 {},
   152  		"mac":                  {},
   153  		"url":                  {},
   154  		"domain":               {},
   155  		"length":               {},
   156  		"min-length":           {},
   157  		"max-length":           {},
   158  		"size":                 {},
   159  		"between":              {},
   160  		"min":                  {},
   161  		"max":                  {},
   162  		"json":                 {},
   163  		"integer":              {},
   164  		"float":                {},
   165  		"boolean":              {},
   166  		"same":                 {},
   167  		"different":            {},
   168  		"in":                   {},
   169  		"not-in":               {},
   170  		"regex":                {},
   171  	}
   172  	// boolMap defines the boolean values.
   173  	boolMap = map[string]struct{}{
   174  		"1":     {},
   175  		"true":  {},
   176  		"on":    {},
   177  		"yes":   {},
   178  		"":      {},
   179  		"0":     {},
   180  		"false": {},
   181  		"off":   {},
   182  		"no":    {},
   183  	}
   184  	// defaultMessages is the default error messages.
   185  	// Note that these messages are synchronized from ./i18n/en/validation.toml .
   186  	defaultMessages = map[string]string{
   187  		"required":              "The :attribute field is required",
   188  		"required-if":           "The :attribute field is required",
   189  		"required-unless":       "The :attribute field is required",
   190  		"required-with":         "The :attribute field is required",
   191  		"required-with-all":     "The :attribute field is required",
   192  		"required-without":      "The :attribute field is required",
   193  		"required-without-all":  "The :attribute field is required",
   194  		"date":                  "The :attribute value is not a valid date",
   195  		"date-format":           "The :attribute value does not match the format :format",
   196  		"email":                 "The :attribute value must be a valid email address",
   197  		"phone":                 "The :attribute value must be a valid phone number",
   198  		"telephone":             "The :attribute value must be a valid telephone number",
   199  		"passport":              "The :attribute value is not a valid passport format",
   200  		"password":              "The :attribute value is not a valid passport format",
   201  		"password2":             "The :attribute value is not a valid passport format",
   202  		"password3":             "The :attribute value is not a valid passport format",
   203  		"postcode":              "The :attribute value is not a valid passport format",
   204  		"resident-id":           "The :attribute value is not a valid resident id number",
   205  		"bank-card":             "The :attribute value must be a valid bank card number",
   206  		"qq":                    "The :attribute value must be a valid QQ number",
   207  		"ip":                    "The :attribute value must be a valid IP address",
   208  		"ipv4":                  "The :attribute value must be a valid IPv4 address",
   209  		"ipv6":                  "The :attribute value must be a valid IPv6 address",
   210  		"mac":                   "The :attribute value must be a valid MAC address",
   211  		"url":                   "The :attribute value must be a valid URL address",
   212  		"domain":                "The :attribute value must be a valid domain format",
   213  		"length":                "The :attribute value length must be between :min and :max",
   214  		"min-length":            "The :attribute value length must be equal or greater than :min",
   215  		"max-length":            "The :attribute value length must be equal or lesser than :max",
   216  		"size":                  "The :attribute value length must be :size",
   217  		"between":               "The :attribute value must be between :min and :max",
   218  		"min":                   "The :attribute value must be equal or greater than :min",
   219  		"max":                   "The :attribute value must be equal or lesser than :max",
   220  		"json":                  "The :attribute value must be a valid JSON string",
   221  		"xml":                   "The :attribute value must be a valid XML string",
   222  		"array":                 "The :attribute value must be an array",
   223  		"integer":               "The :attribute value must be an integer",
   224  		"float":                 "The :attribute value must be a float",
   225  		"boolean":               "The :attribute value field must be true or false",
   226  		"same":                  "The :attribute value must be the same as field :field",
   227  		"different":             "The :attribute value must be different from field :field",
   228  		"in":                    "The :attribute value is not in acceptable range",
   229  		"not-in":                "The :attribute value is not in acceptable range",
   230  		"regex":                 "The :attribute value is invalid",
   231  		internalDefaultRuleName: "The :attribute value is invalid",
   232  	}
   233  	// markedRuleMap defines all rules that are just marked rules which have neither functional meaning
   234  	// nor error messages.
   235  	markedRuleMap = map[string]bool{
   236  		bailRuleName: true,
   237  		//"nullable":   true,
   238  	}
   239  )
   240  
   241  // CheckValue checks single value with specified rules.
   242  // It returns nil if successful validation.
   243  //
   244  // The parameter `value` can be any type of variable, which will be converted to string
   245  // for validation.
   246  // The parameter `rules` can be one or more rules, multiple rules joined using char '|'.
   247  // The parameter `messages` specifies the custom error messages, which can be type of:
   248  // string/map/struct/*struct.
   249  // The optional parameter `params` specifies the extra validation parameters for some rules
   250  // like: required-*、same、different, etc.
   251  func CheckValue(ctx context.Context, value interface{}, rules string, messages interface{}, params ...interface{}) Error {
   252  	var data interface{}
   253  	if len(params) > 0 {
   254  		data = params[0]
   255  	}
   256  	return defaultValidator.Ctx(ctx).Rules(rules).Data(data).Messages(messages).CheckValue(value)
   257  }
   258  
   259  // CheckMap validates map and returns the error result. It returns nil if with successful validation.
   260  //
   261  // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
   262  // if `rules` is type of []string.
   263  // The optional parameter `messages` specifies the custom error messages for specified keys and rules.
   264  func CheckMap(ctx context.Context, params interface{}, rules interface{}, messages ...CustomMsg) Error {
   265  	var customErrorMessages CustomMsg
   266  	if len(messages) > 0 {
   267  		customErrorMessages = messages[0]
   268  	}
   269  	return defaultValidator.Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckMap(params)
   270  }
   271  
   272  // CheckStruct validates struct and returns the error result.
   273  //
   274  // The parameter `object` should be type of struct/*struct.
   275  // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
   276  // if `rules` is type of []string.
   277  // The optional parameter `messages` specifies the custom error messages for specified keys and rules.
   278  func CheckStruct(ctx context.Context, object interface{}, rules interface{}, messages ...CustomMsg) Error {
   279  	var customErrorMessages CustomMsg
   280  	if len(messages) > 0 {
   281  		customErrorMessages = messages[0]
   282  	}
   283  	return defaultValidator.Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckStruct(object)
   284  }
   285  
   286  // CheckStructWithData validates struct with given parameter map and returns the error result.
   287  //
   288  // The parameter `object` should be type of struct/*struct.
   289  // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
   290  // if `rules` is type of []string.
   291  // The optional parameter `messages` specifies the custom error messages for specified keys and rules.
   292  func CheckStructWithData(ctx context.Context, object interface{}, data interface{}, rules interface{}, messages ...CustomMsg) Error {
   293  	var customErrorMessages CustomMsg
   294  	if len(messages) > 0 {
   295  		customErrorMessages = messages[0]
   296  	}
   297  	return defaultValidator.Ctx(ctx).Data(data).Rules(rules).Messages(customErrorMessages).CheckStruct(object)
   298  }
   299  
   300  // parseSequenceTag parses one sequence tag to field, rule and error message.
   301  // The sequence tag is like: [alias@]rule[...#msg...]
   302  func parseSequenceTag(tag string) (field, rule, msg string) {
   303  	// Complete sequence tag.
   304  	// Example: name@required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致
   305  	match, _ := gregex.MatchString(`\s*((\w+)\s*@){0,1}\s*([^#]+)\s*(#\s*(.*)){0,1}\s*`, tag)
   306  	return strings.TrimSpace(match[2]), strings.TrimSpace(match[3]), strings.TrimSpace(match[5])
   307  }