123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
- //
- // This Source Code Form is subject to the terms of the MIT License.
- // If a copy of the MIT was not distributed with this file,
- // You can obtain one at https://github.com/gogf/gf.
- // Package gvalid implements powerful and useful data/form validation functionality.
- package gvalid
- import (
- "context"
- "regexp"
- "strings"
- "github.com/gogf/gf/text/gregex"
- )
- // Refer to Laravel validation:
- // https://laravel.com/docs/5.5/validation#available-validation-rules
- // https://learnku.com/docs/laravel/5.4/validation
- //
- // All supported rules:
- // required format: required brief: Required.
- // required-if format: required-if:field,value,... brief: Required unless all given field and its value are equal.
- // required-unless format: required-unless:field,value,... brief: Required unless all given field and its value are not equal.
- // required-with format: required-with:field1,field2,... brief: Required if any of given fields are not empty.
- // required-with-all format: required-with-all:field1,field2,... brief: Required if all given fields are not empty.
- // required-without format: required-without:field1,field2,... brief: Required if any of given fields are empty.
- // required-without-all format: required-without-all:field1,field2,...brief: Required if all given fields are empty.
- // bail format: bail brief: Stop validating when this field's validation failed.
- // date format: date brief: Standard date, like: 2006-01-02, 20060102, 2006.01.02
- // date-format format: date-format:format brief: Custom date format.
- // email format: email brief: Email address.
- // phone format: phone brief: Phone number.
- // telephone format: telephone brief: Telephone number, like: "XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
- // passport format: passport brief: Universal passport format rule: Starting with letter, containing only numbers or underscores, length between 6 and 18
- // password format: password brief: Universal password format rule1: Containing any visible chars, length between 6 and 18.
- // password2 format: password2 brief: Universal password format rule2: Must meet password rule1, must contain lower and upper letters and numbers.
- // password3 format: password3 brief: Universal password format rule3: Must meet password rule1, must contain lower and upper letters, numbers and special chars.
- // postcode format: postcode brief: Postcode number.
- // resident-id format: resident-id brief: Resident id number.
- // bank-card format: bank-card brief: Bank card nunber.
- // qq format: qq brief: Tencent QQ number.
- // ip format: ip brief: IPv4/IPv6.
- // ipv4 format: ipv4 brief: IPv4.
- // ipv6 format: ipv6 brief: IPv6.
- // mac format: mac brief: MAC.
- // url format: url brief: URL.
- // domain format: domain brief: Domain.
- // 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.
- // 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.
- // 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.
- // 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.
- // between format: between:min,max brief: Range between :min and :max. It supports both integer and float.
- // min format: min:min brief: Equal or greater than :min. It supports both integer and float.
- // max format: max:max brief: Equal or lesser than :max. It supports both integer and float.
- // json format: json brief: JSON.
- // integer format: integer brief: Integer.
- // float format: float brief: Float. Note that an integer is actually a float number.
- // boolean format: boolean brief: Boolean(1,true,on,yes:true | 0,false,off,no,"":false)
- // same format: same:field brief: Value should be the same as value of field.
- // different format: different:field brief: Value should be different from value of field.
- // in format: in:value1,value2,... brief: Value should be in: value1,value2,...
- // not-in format: not-in:value1,value2,... brief: Value should not be in: value1,value2,...
- // regex format: regex:pattern brief: Value should match custom regular expression pattern.
- // CustomMsg is the custom error message type,
- // like: map[field] => string|map[rule]string
- type CustomMsg = map[string]interface{}
- // fieldRule defined the alias name and rule string for specified field.
- type fieldRule struct {
- Name string // Alias name for the field.
- Rule string // Rule string like: "max:6"
- }
- // apiNoValidation is an interface that marks current struct not validated by package `gvalid`.
- type apiNoValidation interface {
- NoValidation()
- }
- const (
- singleRulePattern = `^([\w-]+):{0,1}(.*)` // regular expression pattern for single validation rule.
- internalRulesErrRuleName = "InvalidRules" // rule name for internal invalid rules validation error.
- internalParamsErrRuleName = "InvalidParams" // rule name for internal invalid params validation error.
- internalObjectErrRuleName = "InvalidObject" // rule name for internal invalid object validation error.
- internalErrorMapKey = "__InternalError__" // error map key for internal errors.
- internalDefaultRuleName = "__default__" // default rule name for i18n error message format if no i18n message found for specified error rule.
- ruleMessagePrefixForI18n = "gf.gvalid.rule." // prefix string for each rule configuration in i18n content.
- noValidationTagName = "nv" // no validation tag name for struct attribute.
- bailRuleName = "bail" // the name for rule "bail"
- )
- var (
- defaultValidator = New() // defaultValidator is the default validator for package functions.
- structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array.
- aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array.
- // all internal error keys.
- internalErrKeyMap = map[string]string{
- internalRulesErrRuleName: internalRulesErrRuleName,
- internalParamsErrRuleName: internalParamsErrRuleName,
- internalObjectErrRuleName: internalObjectErrRuleName,
- }
- // regular expression object for single rule
- // which is compiled just once and of repeatable usage.
- ruleRegex, _ = regexp.Compile(singleRulePattern)
- // mustCheckRulesEvenValueEmpty specifies some rules that must be validated
- // even the value is empty (nil or empty).
- mustCheckRulesEvenValueEmpty = map[string]struct{}{
- "required": {},
- "required-if": {},
- "required-unless": {},
- "required-with": {},
- "required-with-all": {},
- "required-without": {},
- "required-without-all": {},
- //"same": {},
- //"different": {},
- //"in": {},
- //"not-in": {},
- //"regex": {},
- }
- // allSupportedRules defines all supported rules that is used for quick checks.
- allSupportedRules = map[string]struct{}{
- "required": {},
- "required-if": {},
- "required-unless": {},
- "required-with": {},
- "required-with-all": {},
- "required-without": {},
- "required-without-all": {},
- "bail": {},
- "date": {},
- "date-format": {},
- "email": {},
- "phone": {},
- "phone-loose": {},
- "telephone": {},
- "passport": {},
- "password": {},
- "password2": {},
- "password3": {},
- "postcode": {},
- "resident-id": {},
- "bank-card": {},
- "qq": {},
- "ip": {},
- "ipv4": {},
- "ipv6": {},
- "mac": {},
- "url": {},
- "domain": {},
- "length": {},
- "min-length": {},
- "max-length": {},
- "size": {},
- "between": {},
- "min": {},
- "max": {},
- "json": {},
- "integer": {},
- "float": {},
- "boolean": {},
- "same": {},
- "different": {},
- "in": {},
- "not-in": {},
- "regex": {},
- }
- // boolMap defines the boolean values.
- boolMap = map[string]struct{}{
- "1": {},
- "true": {},
- "on": {},
- "yes": {},
- "": {},
- "0": {},
- "false": {},
- "off": {},
- "no": {},
- }
- // defaultMessages is the default error messages.
- // Note that these messages are synchronized from ./i18n/en/validation.toml .
- defaultMessages = map[string]string{
- "required": "The :attribute field is required",
- "required-if": "The :attribute field is required",
- "required-unless": "The :attribute field is required",
- "required-with": "The :attribute field is required",
- "required-with-all": "The :attribute field is required",
- "required-without": "The :attribute field is required",
- "required-without-all": "The :attribute field is required",
- "date": "The :attribute value is not a valid date",
- "date-format": "The :attribute value does not match the format :format",
- "email": "The :attribute value must be a valid email address",
- "phone": "The :attribute value must be a valid phone number",
- "telephone": "The :attribute value must be a valid telephone number",
- "passport": "The :attribute value is not a valid passport format",
- "password": "The :attribute value is not a valid passport format",
- "password2": "The :attribute value is not a valid passport format",
- "password3": "The :attribute value is not a valid passport format",
- "postcode": "The :attribute value is not a valid passport format",
- "resident-id": "The :attribute value is not a valid resident id number",
- "bank-card": "The :attribute value must be a valid bank card number",
- "qq": "The :attribute value must be a valid QQ number",
- "ip": "The :attribute value must be a valid IP address",
- "ipv4": "The :attribute value must be a valid IPv4 address",
- "ipv6": "The :attribute value must be a valid IPv6 address",
- "mac": "The :attribute value must be a valid MAC address",
- "url": "The :attribute value must be a valid URL address",
- "domain": "The :attribute value must be a valid domain format",
- "length": "The :attribute value length must be between :min and :max",
- "min-length": "The :attribute value length must be equal or greater than :min",
- "max-length": "The :attribute value length must be equal or lesser than :max",
- "size": "The :attribute value length must be :size",
- "between": "The :attribute value must be between :min and :max",
- "min": "The :attribute value must be equal or greater than :min",
- "max": "The :attribute value must be equal or lesser than :max",
- "json": "The :attribute value must be a valid JSON string",
- "xml": "The :attribute value must be a valid XML string",
- "array": "The :attribute value must be an array",
- "integer": "The :attribute value must be an integer",
- "float": "The :attribute value must be a float",
- "boolean": "The :attribute value field must be true or false",
- "same": "The :attribute value must be the same as field :field",
- "different": "The :attribute value must be different from field :field",
- "in": "The :attribute value is not in acceptable range",
- "not-in": "The :attribute value is not in acceptable range",
- "regex": "The :attribute value is invalid",
- internalDefaultRuleName: "The :attribute value is invalid",
- }
- // markedRuleMap defines all rules that are just marked rules which have neither functional meaning
- // nor error messages.
- markedRuleMap = map[string]bool{
- bailRuleName: true,
- //"nullable": true,
- }
- )
- // CheckValue checks single value with specified rules.
- // It returns nil if successful validation.
- //
- // The parameter `value` can be any type of variable, which will be converted to string
- // for validation.
- // The parameter `rules` can be one or more rules, multiple rules joined using char '|'.
- // The parameter `messages` specifies the custom error messages, which can be type of:
- // string/map/struct/*struct.
- // The optional parameter `params` specifies the extra validation parameters for some rules
- // like: required-*、same、different, etc.
- func CheckValue(ctx context.Context, value interface{}, rules string, messages interface{}, params ...interface{}) Error {
- var data interface{}
- if len(params) > 0 {
- data = params[0]
- }
- return defaultValidator.Ctx(ctx).Rules(rules).Data(data).Messages(messages).CheckValue(value)
- }
- // CheckMap validates map and returns the error result. It returns nil if with successful validation.
- //
- // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
- // if `rules` is type of []string.
- // The optional parameter `messages` specifies the custom error messages for specified keys and rules.
- func CheckMap(ctx context.Context, params interface{}, rules interface{}, messages ...CustomMsg) Error {
- var customErrorMessages CustomMsg
- if len(messages) > 0 {
- customErrorMessages = messages[0]
- }
- return defaultValidator.Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckMap(params)
- }
- // CheckStruct validates struct and returns the error result.
- //
- // The parameter `object` should be type of struct/*struct.
- // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
- // if `rules` is type of []string.
- // The optional parameter `messages` specifies the custom error messages for specified keys and rules.
- func CheckStruct(ctx context.Context, object interface{}, rules interface{}, messages ...CustomMsg) Error {
- var customErrorMessages CustomMsg
- if len(messages) > 0 {
- customErrorMessages = messages[0]
- }
- return defaultValidator.Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckStruct(object)
- }
- // CheckStructWithData validates struct with given parameter map and returns the error result.
- //
- // The parameter `object` should be type of struct/*struct.
- // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
- // if `rules` is type of []string.
- // The optional parameter `messages` specifies the custom error messages for specified keys and rules.
- func CheckStructWithData(ctx context.Context, object interface{}, data interface{}, rules interface{}, messages ...CustomMsg) Error {
- var customErrorMessages CustomMsg
- if len(messages) > 0 {
- customErrorMessages = messages[0]
- }
- return defaultValidator.Ctx(ctx).Data(data).Rules(rules).Messages(customErrorMessages).CheckStruct(object)
- }
- // parseSequenceTag parses one sequence tag to field, rule and error message.
- // The sequence tag is like: [alias@]rule[...#msg...]
- func parseSequenceTag(tag string) (field, rule, msg string) {
- // Complete sequence tag.
- // Example: name@required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致
- match, _ := gregex.MatchString(`\s*((\w+)\s*@){0,1}\s*([^#]+)\s*(#\s*(.*)){0,1}\s*`, tag)
- return strings.TrimSpace(match[2]), strings.TrimSpace(match[3]), strings.TrimSpace(match[5])
- }
|