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 }