gvalid.go 17 KB

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