validator.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /**
  2. * Package validator
  3. *
  4. * MISC:
  5. * - anonymous structs - they don't have names so expect the Struct name within StructValidationErrors to be blank
  6. *
  7. */
  8. package validator
  9. import (
  10. "bytes"
  11. "errors"
  12. "fmt"
  13. "reflect"
  14. "strings"
  15. "time"
  16. "unicode"
  17. )
  18. const (
  19. tagSeparator = ","
  20. orSeparator = "|"
  21. noValidationTag = "-"
  22. tagKeySeparator = "="
  23. structOnlyTag = "structonly"
  24. omitempty = "omitempty"
  25. validationFieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n"
  26. validationStructErrMsg = "Struct:%s\n"
  27. )
  28. // FieldValidationError contains a single field's validation error along
  29. // with other properties that may be needed for error message creation
  30. type FieldValidationError struct {
  31. Field string
  32. ErrorTag string
  33. Kind reflect.Kind
  34. Type reflect.Type
  35. Param string
  36. Value interface{}
  37. }
  38. // This is intended for use in development + debugging and not intended to be a production error message.
  39. // it also allows FieldValidationError to be used as an Error interface
  40. func (e *FieldValidationError) Error() string {
  41. return fmt.Sprintf(validationFieldErrMsg, e.Field, e.ErrorTag)
  42. }
  43. // StructValidationErrors is hierarchical list of field and struct validation errors
  44. // for a non hierarchical representation please see the Flatten method for StructValidationErrors
  45. type StructValidationErrors struct {
  46. // Name of the Struct
  47. Struct string
  48. // Struct Field Errors
  49. Errors map[string]*FieldValidationError
  50. // Struct Fields of type struct and their errors
  51. // key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank
  52. StructErrors map[string]*StructValidationErrors
  53. }
  54. // This is intended for use in development + debugging and not intended to be a production error message.
  55. // it also allows StructValidationErrors to be used as an Error interface
  56. func (e *StructValidationErrors) Error() string {
  57. buff := bytes.NewBufferString(fmt.Sprintf(validationStructErrMsg, e.Struct))
  58. for _, err := range e.Errors {
  59. buff.WriteString(err.Error())
  60. }
  61. for _, err := range e.StructErrors {
  62. buff.WriteString(err.Error())
  63. }
  64. buff.WriteString("\n\n")
  65. return buff.String()
  66. }
  67. // Flatten flattens the StructValidationErrors hierarchical structure into a flat namespace style field name
  68. // for those that want/need it
  69. func (e *StructValidationErrors) Flatten() map[string]*FieldValidationError {
  70. if e == nil {
  71. return nil
  72. }
  73. errs := map[string]*FieldValidationError{}
  74. for _, f := range e.Errors {
  75. errs[f.Field] = f
  76. }
  77. for key, val := range e.StructErrors {
  78. otherErrs := val.Flatten()
  79. for _, f2 := range otherErrs {
  80. f2.Field = fmt.Sprintf("%s.%s", key, f2.Field)
  81. errs[f2.Field] = f2
  82. }
  83. }
  84. return errs
  85. }
  86. // ValidationFunc accepts all values needed for file and cross field validation
  87. // top = top level struct when validating by struct otherwise nil
  88. // current = current level struct when validating by struct otherwise optional comparison value
  89. // f = field value for validation
  90. // param = parameter used in validation i.e. gt=0 param would be 0
  91. type ValidationFunc func(top interface{}, current interface{}, f interface{}, param string) bool
  92. // Validator implements the Validator Struct
  93. // NOTE: Fields within are not thread safe and that is on purpose
  94. // Functions and Tags should all be predifined before use, so subscribe to the philosiphy
  95. // or make it thread safe on your end
  96. type Validator struct {
  97. // tagName being used.
  98. tagName string
  99. // validationFuncs is a map of validation functions and the tag keys
  100. validationFuncs map[string]ValidationFunc
  101. }
  102. // NewValidator creates a new Validator instance for use.
  103. func NewValidator(tagName string, funcs map[string]ValidationFunc) *Validator {
  104. return &Validator{
  105. tagName: tagName,
  106. validationFuncs: funcs,
  107. }
  108. }
  109. // SetTag sets tagName of the Validator to one of your choosing after creation
  110. // perhaps to dodge a tag name conflict in a specific section of code
  111. func (v *Validator) SetTag(tagName string) {
  112. v.tagName = tagName
  113. }
  114. // AddFunction adds a ValidationFunc to a Validator's map of validators denoted by the key
  115. // NOTE: if the key already exists, it will get replaced.
  116. func (v *Validator) AddFunction(key string, f ValidationFunc) error {
  117. if len(key) == 0 {
  118. return errors.New("Function Key cannot be empty")
  119. }
  120. if f == nil {
  121. return errors.New("Function cannot be empty")
  122. }
  123. v.validationFuncs[key] = f
  124. return nil
  125. }
  126. // ValidateStruct validates a struct, even it's nested structs, and returns a struct containing the errors
  127. // NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
  128. // way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
  129. // the Array or Map.
  130. func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
  131. return v.validateStructRecursive(s, s, s)
  132. }
  133. // validateStructRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors
  134. func (v *Validator) validateStructRecursive(top interface{}, current interface{}, s interface{}) *StructValidationErrors {
  135. structValue := reflect.ValueOf(s)
  136. structType := reflect.TypeOf(s)
  137. structName := structType.Name()
  138. if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
  139. return v.validateStructRecursive(top, current, structValue.Elem().Interface())
  140. }
  141. if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
  142. panic("interface passed for validation is not a struct")
  143. }
  144. validationErrors := &StructValidationErrors{
  145. Struct: structName,
  146. Errors: map[string]*FieldValidationError{},
  147. StructErrors: map[string]*StructValidationErrors{},
  148. }
  149. var numFields = structValue.NumField()
  150. for i := 0; i < numFields; i++ {
  151. valueField := structValue.Field(i)
  152. typeField := structType.Field(i)
  153. if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
  154. valueField = valueField.Elem()
  155. }
  156. tag := typeField.Tag.Get(v.tagName)
  157. if tag == noValidationTag {
  158. continue
  159. }
  160. // if no validation and not a struct (which may containt fields for validation)
  161. if tag == "" && valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface {
  162. continue
  163. }
  164. switch valueField.Kind() {
  165. case reflect.Struct, reflect.Interface:
  166. if !unicode.IsUpper(rune(typeField.Name[0])) {
  167. continue
  168. }
  169. if valueField.Type() == reflect.TypeOf(time.Time{}) {
  170. if fieldError := v.validateFieldByNameAndTagAndValue(top, current, valueField.Interface(), typeField.Name, tag); fieldError != nil {
  171. validationErrors.Errors[fieldError.Field] = fieldError
  172. // free up memory reference
  173. fieldError = nil
  174. }
  175. } else {
  176. if strings.Contains(tag, structOnlyTag) {
  177. continue
  178. }
  179. if structErrors := v.validateStructRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
  180. validationErrors.StructErrors[typeField.Name] = structErrors
  181. // free up memory map no longer needed
  182. structErrors = nil
  183. }
  184. }
  185. default:
  186. if fieldError := v.validateFieldByNameAndTagAndValue(top, current, valueField.Interface(), typeField.Name, tag); fieldError != nil {
  187. validationErrors.Errors[fieldError.Field] = fieldError
  188. // free up memory reference
  189. fieldError = nil
  190. }
  191. }
  192. }
  193. if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
  194. return nil
  195. }
  196. return validationErrors
  197. }
  198. // ValidateFieldByTag allows validation of a single field, still using tag style validation to check multiple errors
  199. func (v *Validator) ValidateFieldByTag(f interface{}, tag string) *FieldValidationError {
  200. return v.ValidateFieldByTagAndValue(nil, f, tag)
  201. }
  202. // ValidateFieldByTagAndValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
  203. func (v *Validator) ValidateFieldByTagAndValue(val interface{}, f interface{}, tag string) *FieldValidationError {
  204. return v.validateFieldByNameAndTagAndValue(nil, val, f, "", tag)
  205. }
  206. func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, current interface{}, f interface{}, name string, tag string) *FieldValidationError {
  207. // This is a double check if coming from ValidateStruct but need to be here in case function is called directly
  208. if tag == noValidationTag {
  209. return nil
  210. }
  211. if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") {
  212. return nil
  213. }
  214. valueField := reflect.ValueOf(f)
  215. fieldKind := valueField.Kind()
  216. if fieldKind == reflect.Ptr && !valueField.IsNil() {
  217. return v.validateFieldByNameAndTagAndValue(val, current, valueField.Elem().Interface(), name, tag)
  218. }
  219. fieldType := valueField.Type()
  220. switch fieldKind {
  221. case reflect.Struct, reflect.Interface, reflect.Invalid:
  222. if fieldType != reflect.TypeOf(time.Time{}) {
  223. panic("Invalid field passed to ValidateFieldWithTag")
  224. }
  225. }
  226. var valErr *FieldValidationError
  227. var err error
  228. valTags := strings.Split(tag, tagSeparator)
  229. for _, valTag := range valTags {
  230. orVals := strings.Split(valTag, orSeparator)
  231. if len(orVals) > 1 {
  232. errTag := ""
  233. for _, val := range orVals {
  234. valErr, err = v.validateFieldByNameAndSingleTag(val, current, f, name, val)
  235. if err == nil {
  236. return nil
  237. }
  238. errTag += orSeparator + valErr.ErrorTag
  239. }
  240. errTag = strings.TrimLeft(errTag, orSeparator)
  241. valErr.ErrorTag = errTag
  242. valErr.Kind = fieldKind
  243. return valErr
  244. }
  245. if valErr, err = v.validateFieldByNameAndSingleTag(val, current, f, name, valTag); err != nil {
  246. valErr.Kind = valueField.Kind()
  247. valErr.Type = fieldType
  248. return valErr
  249. }
  250. }
  251. return nil
  252. }
  253. func (v *Validator) validateFieldByNameAndSingleTag(val interface{}, current interface{}, f interface{}, name string, valTag string) (*FieldValidationError, error) {
  254. vals := strings.Split(valTag, tagKeySeparator)
  255. key := strings.Trim(vals[0], " ")
  256. if len(key) == 0 {
  257. panic(fmt.Sprintf("Invalid validation tag on field %s", name))
  258. }
  259. valErr := &FieldValidationError{
  260. Field: name,
  261. ErrorTag: key,
  262. Value: f,
  263. Param: "",
  264. }
  265. // OK to continue because we checked it's existance before getting into this loop
  266. if key == omitempty {
  267. return valErr, nil
  268. }
  269. valFunc, ok := v.validationFuncs[key]
  270. if !ok {
  271. panic(fmt.Sprintf("Undefined validation function on field %s", name))
  272. }
  273. param := ""
  274. if len(vals) > 1 {
  275. param = strings.Trim(vals[1], " ")
  276. }
  277. if err := valFunc(val, current, f, param); !err {
  278. valErr.Param = param
  279. return valErr, errors.New(key)
  280. }
  281. return valErr, nil
  282. }