gvalid_check_struct.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Copyright 2017-2018 gf Author(https://github.com/gogf/gf). 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
  7. import (
  8. "strings"
  9. "github.com/gogf/gf/internal/structs"
  10. "github.com/gogf/gf/util/gconv"
  11. )
  12. var (
  13. structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array.
  14. aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array.
  15. )
  16. // CheckStruct validates strcut and returns the error result.
  17. //
  18. // The parameter <object> should be type of struct/*struct.
  19. // The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
  20. // if <rules> is type of []string.
  21. // The optional parameter <messages> specifies the custom error messages for specified keys and rules.
  22. func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
  23. // It here must use structs.TagFields not structs.MapField to ensure error sequence.
  24. tagField, err := structs.TagFields(object, structTagPriority)
  25. if err != nil {
  26. return newErrorStr("invalid_object", err.Error())
  27. }
  28. // If there's no struct tag and validation rules, it does nothing and returns quickly.
  29. if len(tagField) == 0 && rules == nil {
  30. return nil
  31. }
  32. var (
  33. params = make(map[string]interface{})
  34. checkRules = make(map[string]string)
  35. customMessage = make(CustomMsg)
  36. fieldAliases = make(map[string]string) // Alias names for <messages> overwriting struct tag names.
  37. errorRules = make([]string, 0) // Sequence rules.
  38. errorMaps = make(ErrorMap) // Returned error
  39. )
  40. switch v := rules.(type) {
  41. // Sequence tag: []sequence tag
  42. // Sequence has order for error results.
  43. case []string:
  44. for _, tag := range v {
  45. name, rule, msg := parseSequenceTag(tag)
  46. if len(name) == 0 {
  47. continue
  48. }
  49. if len(msg) > 0 {
  50. var (
  51. msgArray = strings.Split(msg, "|")
  52. ruleArray = strings.Split(rule, "|")
  53. )
  54. for k, v := range ruleArray {
  55. // If length of custom messages is lesser than length of rules,
  56. // the rest rules use the default error messages.
  57. if len(msgArray) <= k {
  58. continue
  59. }
  60. if len(msgArray[k]) == 0 {
  61. continue
  62. }
  63. array := strings.Split(v, ":")
  64. if _, ok := customMessage[name]; !ok {
  65. customMessage[name] = make(map[string]string)
  66. }
  67. customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
  68. }
  69. }
  70. checkRules[name] = rule
  71. errorRules = append(errorRules, name+"@"+rule)
  72. }
  73. // Map type rules does not support sequence.
  74. // Format: map[key]rule
  75. case map[string]string:
  76. checkRules = v
  77. }
  78. // If there's no struct tag and validation rules, it does nothing and returns quickly.
  79. if len(tagField) == 0 && len(checkRules) == 0 {
  80. return nil
  81. }
  82. // Checks and extends the parameters map with struct alias tag.
  83. mapField, err := structs.MapField(object, aliasNameTagPriority)
  84. if err != nil {
  85. return newErrorStr("invalid_object", err.Error())
  86. }
  87. for nameOrTag, field := range mapField {
  88. params[nameOrTag] = field.Value()
  89. params[field.Name()] = field.Value()
  90. }
  91. for _, field := range tagField {
  92. fieldName := field.Name()
  93. // sequence tag == struct tag
  94. // The name here is alias of field name.
  95. name, rule, msg := parseSequenceTag(field.TagValue)
  96. if len(name) == 0 {
  97. name = fieldName
  98. } else {
  99. fieldAliases[fieldName] = name
  100. }
  101. // It here extends the params map using alias names.
  102. if _, ok := params[name]; !ok {
  103. params[name] = field.Value()
  104. }
  105. if _, ok := checkRules[name]; !ok {
  106. if _, ok := checkRules[fieldName]; ok {
  107. // If there's alias name,
  108. // use alias name as its key and remove the field name key.
  109. checkRules[name] = checkRules[fieldName]
  110. delete(checkRules, fieldName)
  111. } else {
  112. checkRules[name] = rule
  113. }
  114. errorRules = append(errorRules, name+"@"+rule)
  115. } else {
  116. // The passed rules can overwrite the rules in struct tag.
  117. continue
  118. }
  119. if len(msg) > 0 {
  120. var (
  121. msgArray = strings.Split(msg, "|")
  122. ruleArray = strings.Split(rule, "|")
  123. )
  124. for k, v := range ruleArray {
  125. // If length of custom messages is lesser than length of rules,
  126. // the rest rules use the default error messages.
  127. if len(msgArray) <= k {
  128. continue
  129. }
  130. if len(msgArray[k]) == 0 {
  131. continue
  132. }
  133. array := strings.Split(v, ":")
  134. if _, ok := customMessage[name]; !ok {
  135. customMessage[name] = make(map[string]string)
  136. }
  137. customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
  138. }
  139. }
  140. }
  141. // Custom error messages,
  142. // which have the most priority than <rules> and struct tag.
  143. if len(messages) > 0 && len(messages[0]) > 0 {
  144. for k, v := range messages[0] {
  145. if a, ok := fieldAliases[k]; ok {
  146. // Overwrite the key of field name.
  147. customMessage[a] = v
  148. } else {
  149. customMessage[k] = v
  150. }
  151. }
  152. }
  153. // The following logic is the same as some of CheckMap.
  154. var value interface{}
  155. for key, rule := range checkRules {
  156. value = nil
  157. if v, ok := params[key]; ok {
  158. value = v
  159. }
  160. // It checks each rule and its value in loop.
  161. if e := doCheck(key, value, rule, customMessage[key], params); e != nil {
  162. _, item := e.FirstItem()
  163. // ===========================================================
  164. // Only in map and struct validations, if value is nil or empty
  165. // string and has no required* rules, it clears the error message.
  166. // ===========================================================
  167. if value == nil || gconv.String(value) == "" {
  168. required := false
  169. // rule => error
  170. for k := range item {
  171. // Default required rules.
  172. if _, ok := mustCheckRulesEvenValueEmpty[k]; ok {
  173. required = true
  174. break
  175. }
  176. // Custom rules are also required in default.
  177. if _, ok := customRuleFuncMap[k]; ok {
  178. required = true
  179. break
  180. }
  181. }
  182. if !required {
  183. continue
  184. }
  185. }
  186. if _, ok := errorMaps[key]; !ok {
  187. errorMaps[key] = make(map[string]string)
  188. }
  189. for k, v := range item {
  190. errorMaps[key][k] = v
  191. }
  192. }
  193. }
  194. if len(errorMaps) > 0 {
  195. return newError(errorRules, errorMaps)
  196. }
  197. return nil
  198. }