123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*
- Package validator implements value validations for structs and individual fields based on tags. It can also handle Cross Field validation and even Cross Field Cross Struct validation for nested structs.
- Built In Validator
- myValidator = validator.NewValidator("validate", validator.BakedInValidators)
- errs := myValidator.ValidateStruct(//your struct)
- valErr := myValidator.ValidateFieldByTag(field, "omitempty,min=1,max=10")
- A simple example usage:
- type UserDetail {
- Details string `validate:"-"`
- }
- type User struct {
- Name string `validate:"required,max=60"`
- PreferedName string `validate:"omitempty,max=60"`
- Sub UserDetail
- }
- user := &User {
- Name: "",
- }
- // errs will contain a hierarchical list of errors
- // using the StructValidationErrors struct
- // or nil if no errors exist
- errs := myValidator.ValidateStruct(user)
- // in this case 1 error Name is required
- errs.Struct will be "User"
- errs.StructErrors will be empty <-- fields that were structs
- errs.Errors will have 1 error of type FieldValidationError
- NOTE: Anonymous Structs - they don't have names so expect the Struct name
- within StructValidationErrors to be blank.
- Error Handling
- The error can be used like so
- fieldErr, _ := errs["Name"]
- fieldErr.Field // "Name"
- fieldErr.ErrorTag // "required"
- Both StructValidationErrors and FieldValidationError implement the Error interface but it's
- intended use is for development + debugging, not a production error message.
- fieldErr.Error() // Field validation for "Name" failed on the "required" tag
- errs.Error()
- // Struct: User
- // Field validation for "Name" failed on the "required" tag
- Why not a better error message? because this library intends for you to handle your own error messages
- Why should I handle my own errors? Many reasons, for us building an internationalized application
- I needed to know the field and what validation failed so that I could provide an error in the users specific language.
- if fieldErr.Field == "Name" {
- switch fieldErr.ErrorTag
- case "required":
- return "Translated string based on field + error"
- default:
- return "Translated string based on field"
- }
- The hierarchical error structure is hard to work with sometimes.. Agreed Flatten function to the rescue!
- Flatten will return a map of FieldValidationError's but the field name will be namespaced.
- // if UserDetail Details field failed validation
- Field will be "Sub.Details"
- // for Name
- Field will be "Name"
- Custom Functions
- Custom functions can be added
- //Structure
- func customFunc(top interface{}, current interface{}, field interface{}, param string) bool {
- if whatever {
- return false
- }
- return true
- }
- myValidator.AddFunction("custom tag name", customFunc)
- // NOTES: using the same tag name as an existing function
- // will overwrite the existing one
- Cross Field Validation
- Cross Field Validation can be implemented, for example Start & End Date range validation
- // NOTE: when calling myValidator.validateStruct(val) val will be the top level struct passed
- // into the function
- // when calling myValidator.ValidateFieldByTagAndValue(val, field, tag) val will be
- // whatever you pass, struct, field...
- // when calling myValidator.ValidateFieldByTag(field, tag) val will be nil
- //
- // Because of the specific requirements and field names within each persons project that
- // uses this library it is likely that custom functions will need to be created for your
- // Cross Field Validation needs, however there are some build in Generic Cross Field validations,
- // see Baked In Validators and Tags below
- func isDateRangeValid(val interface{}, field interface{}, param string) bool {
- myStruct := val.(myStructType)
- if myStruct.Start.After(field.(time.Time)) {
- return false
- }
- return true
- }
- Multiple Validators
- Multiple validators on a field will process in the order defined
- type Test struct {
- Field `validate:"max=10,min=1"`
- }
- // max will be checked then min
- Bad Validator definitions are not handled by the library
- type Test struct {
- Field `validate:"min=10,max=0"`
- }
- // this definition of min max will never validate
- Baked In Validators and Tags
- NOTE: Baked In Cross field validation only compares fields on the same struct,
- if cross field + cross struct validation is needed your own custom validator
- should be implemented.
- Here is a list of the current built in validators:
- -
- Tells the validation to skip this struct field; this is particularily
- handy in ignoring embedded structs from being validated. (Usage: -)
- |
- This is the 'or' operator allowing multiple validators to be used and
- accepted. (Usage: rbg|rgba) <-- this would allow either rgb or rgba
- colors to be accepted. This can also be combined with 'and' for example
- ( Usage: omitempty,rgb|rgba)
- structonly
- When a field that is a nest struct in encountered and contains this flag
- any validation on the nested struct such as "required" will be run, but
- none of the nested struct fields will be validated. This is usefull if
- inside of you program you know the struct will be valid, but need to
- verify it has been assigned.
- omitempty
- Allows conitional validation, for example if a field is not set with
- a value (Determined by the required validator) then other validation
- such as min or max won't run, but if a value is set validation will run.
- (Usage: omitempty)
- required
- This validates that the value is not the data types default value.
- For numbers ensures value is not zero. For strings ensures value is
- not "". For slices, arrays, and maps, ensures the length is not zero.
- (Usage: required)
- len
- For numbers, max will ensure that the value is
- equal to the parameter given. For strings, it checks that
- the string length is exactly that number of characters. For slices,
- arrays, and maps, validates the number of items. (Usage: len=10)
- max
- For numbers, max will ensure that the value is
- less than or equal to the parameter given. For strings, it checks
- that the string length is at most that number of characters. For
- slices, arrays, and maps, validates the number of items. (Usage: max=10)
- min
- For numbers, min will ensure that the value is
- greater or equal to the parameter given. For strings, it checks that
- the string length is at least that number of characters. For slices,
- arrays, and maps, validates the number of items. (Usage: min=10)
- gt
- For numbers, this will ensure that the value is greater than the
- parameter given. For strings, it checks that the string length
- is greater than that number of characters. For slices, arrays
- and maps it validates the number of items. (Usage: gt=10)
- For time.Time ensures the time value is greater than time.Now.UTC()
- (Usage: gt)
- gte
- Same as 'min' above. Kept both to make terminology with 'len' easier
- (Usage: gte=10)
- For time.Time ensures the time value is greater than or equal to time.Now.UTC()
- (Usage: gte)
- lt
- For numbers, this will ensure that the value is
- less than the parameter given. For strings, it checks
- that the string length is less than that number of characters.
- For slices, arrays, and maps it validates the number of items.
- (Usage: lt=10)
- For time.Time ensures the time value is less than time.Now.UTC()
- (Usage: lt)
- lte
- Same as 'max' above. Kept both to make terminology with 'len' easier
- (Usage: lte=10)
- For time.Time ensures the time value is less than or equal to time.Now.UTC()
- (Usage: lte)
- gtfield
- Only valid for Numbers and time.Time types, this will validate the field value
- against another fields value either within a struct or passed in field.
- usage examples are for validation of a Start and End date:
- Validation on End field using ValidateByStruct Usage(gtfield=Start)
- Validating by field ValidateFieldByTagAndValue(start, end, "gtfield")
- gtefield
- Only valid for Numbers and time.Time types, this will validate the field value
- against another fields value either within a struct or passed in field.
- usage examples are for validation of a Start and End date:
- Validation on End field using ValidateByStruct Usage(gtefield=Start)
- Validating by field ValidateFieldByTagAndValue(start, end, "gtefield")
- ltfield
- Only valid for Numbers and time.Time types, this will validate the field value
- against another fields value either within a struct or passed in field.
- usage examples are for validation of a Start and End date:
- Validation on End field using ValidateByStruct Usage(ltfield=Start)
- Validating by field ValidateFieldByTagAndValue(start, end, "ltfield")
- ltefield
- Only valid for Numbers and time.Time types, this will validate the field value
- against another fields value either within a struct or passed in field.
- usage examples are for validation of a Start and End date:
- Validation on End field using ValidateByStruct Usage(ltefield=Start)
- Validating by field ValidateFieldByTagAndValue(start, end, "ltefield")
- alpha
- This validates that a strings value contains alpha characters only
- (Usage: alpha)
- alphanum
- This validates that a strings value contains alphanumeric characters only
- (Usage: alphanum)
- numeric
- This validates that a strings value contains a basic numeric value.
- basic excludes exponents etc...
- (Usage: numeric)
- hexadecimal
- This validates that a strings value contains a valid hexadecimal.
- (Usage: hexadecimal)
- hexcolor
- This validates that a strings value contains a valid hex color including
- hashtag (#)
- (Usage: hexcolor)
- rgb
- This validates that a strings value contains a valid rgb color
- (Usage: rgb)
- rgba
- This validates that a strings value contains a valid rgba color
- (Usage: rgba)
- hsl
- This validates that a strings value contains a valid hsl color
- (Usage: hsl)
- hsla
- This validates that a strings value contains a valid hsla color
- (Usage: hsla)
- email
- This validates that a strings value contains a valid email
- This may not conform to all possibilities of any rfc standard, but neither
- does any email provider accept all posibilities...
- (Usage: email)
- url
- This validates that a strings value contains a valid url
- This will accept any url the golang request uri accepts but must contain
- a schema for example http:// or rtmp://
- (Usage: url)
- uri
- This validates that a strings value contains a valid uri
- This will accept any uri the golang request uri accepts (Usage: uri)
- Validator notes:
- regex
- a regex validator won't be added because commas and = signs can be part of
- a regex which conflict with the validation definitions, although workarounds
- can be made, they take away from using pure regex's. Furthermore it's quick
- and dirty but the regex's become harder to maintain and are not reusable, so
- it's as much a programming philosiphy as anything.
- In place of this new validator functions should be created; a regex can be
- used within the validator function and even be precompiled for better efficiency
- within regexes.go.
- And the best reason, you can sumit a pull request and we can keep on adding to the
- validation library of this package!
- Panics
- This package panics when bad input is provided, this is by design, bad code like that should not make it to production.
- type Test struct {
- TestField string `validate:"nonexistantfunction=1"`
- }
- t := &Test{
- TestField: "Test"
- }
- myValidator.ValidateStruct(t) // this will panic
- */
- package validator
|