Giter Site home page Giter Site logo

govalidator's Introduction

govalidator

Gitter GoDoc Build Status Coverage Go Report Card GoSearch Backers on Open Collective Sponsors on Open Collective FOSSA Status

A package of validators and sanitizers for strings, structs and collections. Based on validator.js.

Installation

Make sure that Go is installed on your computer. Type the following command in your terminal:

go get github.com/asaskevich/govalidator

or you can get specified release of the package with gopkg.in:

go get gopkg.in/asaskevich/govalidator.v10

After it the package is ready to use.

Import package in your project

Add following line in your *.go file:

import "github.com/asaskevich/govalidator"

If you are unhappy to use long govalidator, you can do something like this:

import (
  valid "github.com/asaskevich/govalidator"
)

Activate behavior to require all fields have a validation tag by default

SetFieldsRequiredByDefault causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using valid:"-" or valid:"email,optional"). A good place to activate this is a package init function or the main() function.

SetNilPtrAllowedByRequired causes validation to pass when struct fields marked by required are set to nil. This is disabled by default for consistency, but some packages that need to be able to determine between nil and zero value state can use this. If disabled, both nil and zero values cause validation errors.

import "github.com/asaskevich/govalidator"

func init() {
  govalidator.SetFieldsRequiredByDefault(true)
}

Here's some code to explain it:

// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
type exampleStruct struct {
  Name  string ``
  Email string `valid:"email"`
}

// this, however, will only fail when Email is empty or an invalid email address:
type exampleStruct2 struct {
  Name  string `valid:"-"`
  Email string `valid:"email"`
}

// lastly, this will only fail when Email is an invalid email address but not when it's empty:
type exampleStruct2 struct {
  Name  string `valid:"-"`
  Email string `valid:"email,optional"`
}

Recent breaking changes (see #123)

Custom validator function signature

A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible.

import "github.com/asaskevich/govalidator"

// old signature
func(i interface{}) bool

// new signature
func(i interface{}, o interface{}) bool
Adding a custom validator

This was changed to prevent data races when accessing custom validators.

import "github.com/asaskevich/govalidator"

// before
govalidator.CustomTypeTagMap["customByteArrayValidator"] = func(i interface{}, o interface{}) bool {
  // ...
}

// after
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", func(i interface{}, o interface{}) bool {
  // ...
})

List of functions:

func Abs(value float64) float64
func BlackList(str, chars string) string
func ByteLength(str string, params ...string) bool
func CamelCaseToUnderscore(str string) string
func Contains(str, substring string) bool
func Count(array []interface{}, iterator ConditionIterator) int
func Each(array []interface{}, iterator Iterator)
func ErrorByField(e error, field string) string
func ErrorsByField(e error) map[string]string
func Filter(array []interface{}, iterator ConditionIterator) []interface{}
func Find(array []interface{}, iterator ConditionIterator) interface{}
func GetLine(s string, index int) (string, error)
func GetLines(s string) []string
func HasLowerCase(str string) bool
func HasUpperCase(str string) bool
func HasWhitespace(str string) bool
func HasWhitespaceOnly(str string) bool
func InRange(value interface{}, left interface{}, right interface{}) bool
func InRangeFloat32(value, left, right float32) bool
func InRangeFloat64(value, left, right float64) bool
func InRangeInt(value, left, right interface{}) bool
func IsASCII(str string) bool
func IsAlpha(str string) bool
func IsAlphanumeric(str string) bool
func IsBase64(str string) bool
func IsByteLength(str string, min, max int) bool
func IsCIDR(str string) bool
func IsCRC32(str string) bool
func IsCRC32b(str string) bool
func IsCreditCard(str string) bool
func IsDNSName(str string) bool
func IsDataURI(str string) bool
func IsDialString(str string) bool
func IsDivisibleBy(str, num string) bool
func IsEmail(str string) bool
func IsExistingEmail(email string) bool
func IsFilePath(str string) (bool, int)
func IsFloat(str string) bool
func IsFullWidth(str string) bool
func IsHalfWidth(str string) bool
func IsHash(str string, algorithm string) bool
func IsHexadecimal(str string) bool
func IsHexcolor(str string) bool
func IsHost(str string) bool
func IsIP(str string) bool
func IsIPv4(str string) bool
func IsIPv6(str string) bool
func IsISBN(str string, version int) bool
func IsISBN10(str string) bool
func IsISBN13(str string) bool
func IsISO3166Alpha2(str string) bool
func IsISO3166Alpha3(str string) bool
func IsISO4217(str string) bool
func IsISO693Alpha2(str string) bool
func IsISO693Alpha3b(str string) bool
func IsIn(str string, params ...string) bool
func IsInRaw(str string, params ...string) bool
func IsInt(str string) bool
func IsJSON(str string) bool
func IsLatitude(str string) bool
func IsLongitude(str string) bool
func IsLowerCase(str string) bool
func IsMAC(str string) bool
func IsMD4(str string) bool
func IsMD5(str string) bool
func IsMagnetURI(str string) bool
func IsMongoID(str string) bool
func IsMultibyte(str string) bool
func IsNatural(value float64) bool
func IsNegative(value float64) bool
func IsNonNegative(value float64) bool
func IsNonPositive(value float64) bool
func IsNotNull(str string) bool
func IsNull(str string) bool
func IsNumeric(str string) bool
func IsPort(str string) bool
func IsPositive(value float64) bool
func IsPrintableASCII(str string) bool
func IsRFC3339(str string) bool
func IsRFC3339WithoutZone(str string) bool
func IsRGBcolor(str string) bool
func IsRegex(str string) bool
func IsRequestURI(rawurl string) bool
func IsRequestURL(rawurl string) bool
func IsRipeMD128(str string) bool
func IsRipeMD160(str string) bool
func IsRsaPub(str string, params ...string) bool
func IsRsaPublicKey(str string, keylen int) bool
func IsSHA1(str string) bool
func IsSHA256(str string) bool
func IsSHA384(str string) bool
func IsSHA512(str string) bool
func IsSSN(str string) bool
func IsSemver(str string) bool
func IsTiger128(str string) bool
func IsTiger160(str string) bool
func IsTiger192(str string) bool
func IsTime(str string, format string) bool
func IsType(v interface{}, params ...string) bool
func IsURL(str string) bool
func IsUTFDigit(str string) bool
func IsUTFLetter(str string) bool
func IsUTFLetterNumeric(str string) bool
func IsUTFNumeric(str string) bool
func IsUUID(str string) bool
func IsUUIDv3(str string) bool
func IsUUIDv4(str string) bool
func IsUUIDv5(str string) bool
func IsULID(str string) bool
func IsUnixTime(str string) bool
func IsUpperCase(str string) bool
func IsVariableWidth(str string) bool
func IsWhole(value float64) bool
func LeftTrim(str, chars string) string
func Map(array []interface{}, iterator ResultIterator) []interface{}
func Matches(str, pattern string) bool
func MaxStringLength(str string, params ...string) bool
func MinStringLength(str string, params ...string) bool
func NormalizeEmail(str string) (string, error)
func PadBoth(str string, padStr string, padLen int) string
func PadLeft(str string, padStr string, padLen int) string
func PadRight(str string, padStr string, padLen int) string
func PrependPathToErrors(err error, path string) error
func Range(str string, params ...string) bool
func RemoveTags(s string) string
func ReplacePattern(str, pattern, replace string) string
func Reverse(s string) string
func RightTrim(str, chars string) string
func RuneLength(str string, params ...string) bool
func SafeFileName(str string) string
func SetFieldsRequiredByDefault(value bool)
func SetNilPtrAllowedByRequired(value bool)
func Sign(value float64) float64
func StringLength(str string, params ...string) bool
func StringMatches(s string, params ...string) bool
func StripLow(str string, keepNewLines bool) string
func ToBoolean(str string) (bool, error)
func ToFloat(str string) (float64, error)
func ToInt(value interface{}) (res int64, err error)
func ToJSON(obj interface{}) (string, error)
func ToString(obj interface{}) string
func Trim(str, chars string) string
func Truncate(str string, length int, ending string) string
func TruncatingErrorf(str string, args ...interface{}) error
func UnderscoreToCamelCase(s string) string
func ValidateMap(inputMap map[string]interface{}, validationMap map[string]interface{}) (bool, error)
func ValidateStruct(s interface{}) (bool, error)
func WhiteList(str, chars string) string
type ConditionIterator
type CustomTypeValidator
type Error
func (e Error) Error() string
type Errors
func (es Errors) Error() string
func (es Errors) Errors() []error
type ISO3166Entry
type ISO693Entry
type InterfaceParamValidator
type Iterator
type ParamValidator
type ResultIterator
type UnsupportedTypeError
func (e *UnsupportedTypeError) Error() string
type Validator

Examples

IsURL
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`))
IsType
println(govalidator.IsType("Bob", "string"))
println(govalidator.IsType(1, "int"))
i := 1
println(govalidator.IsType(&i, "*int"))

IsType can be used through the tag type which is essential for map validation:

type User	struct {
  Name string      `valid:"type(string)"`
  Age  int         `valid:"type(int)"`
  Meta interface{} `valid:"type(string)"`
}
result, err := govalidator.ValidateStruct(User{"Bob", 20, "meta"})
if err != nil {
	println("error: " + err.Error())
}
println(result)
ToString
type User struct {
	FirstName string
	LastName string
}

str := govalidator.ToString(&User{"John", "Juan"})
println(str)
Each, Map, Filter, Count for slices

Each iterates over the slice/array and calls Iterator for every item

data := []interface{}{1, 2, 3, 4, 5}
var fn govalidator.Iterator = func(value interface{}, index int) {
	println(value.(int))
}
govalidator.Each(data, fn)
data := []interface{}{1, 2, 3, 4, 5}
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} {
	return value.(int) * 3
}
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool {
	return value.(int)%2 == 0
}
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
_ = govalidator.Count(data, fn) // result = 5
ValidateStruct #2

If you want to validate structs, you can use tag valid for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place - in your tag. If you need a validator that is not on the list below, you can add it like this:

govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
	return str == "duck"
})

For completely custom validators (interface-based), see below.

Here is a list of available validators for struct fields (validator - used function):

"email":              IsEmail,
"url":                IsURL,
"dialstring":         IsDialString,
"requrl":             IsRequestURL,
"requri":             IsRequestURI,
"alpha":              IsAlpha,
"utfletter":          IsUTFLetter,
"alphanum":           IsAlphanumeric,
"utfletternum":       IsUTFLetterNumeric,
"numeric":            IsNumeric,
"utfnumeric":         IsUTFNumeric,
"utfdigit":           IsUTFDigit,
"hexadecimal":        IsHexadecimal,
"hexcolor":           IsHexcolor,
"rgbcolor":           IsRGBcolor,
"lowercase":          IsLowerCase,
"uppercase":          IsUpperCase,
"int":                IsInt,
"float":              IsFloat,
"null":               IsNull,
"uuid":               IsUUID,
"uuidv3":             IsUUIDv3,
"uuidv4":             IsUUIDv4,
"uuidv5":             IsUUIDv5,
"creditcard":         IsCreditCard,
"isbn10":             IsISBN10,
"isbn13":             IsISBN13,
"json":               IsJSON,
"multibyte":          IsMultibyte,
"ascii":              IsASCII,
"printableascii":     IsPrintableASCII,
"fullwidth":          IsFullWidth,
"halfwidth":          IsHalfWidth,
"variablewidth":      IsVariableWidth,
"base64":             IsBase64,
"datauri":            IsDataURI,
"ip":                 IsIP,
"port":               IsPort,
"ipv4":               IsIPv4,
"ipv6":               IsIPv6,
"dns":                IsDNSName,
"host":               IsHost,
"mac":                IsMAC,
"latitude":           IsLatitude,
"longitude":          IsLongitude,
"ssn":                IsSSN,
"semver":             IsSemver,
"rfc3339":            IsRFC3339,
"rfc3339WithoutZone": IsRFC3339WithoutZone,
"ISO3166Alpha2":      IsISO3166Alpha2,
"ISO3166Alpha3":      IsISO3166Alpha3,
"ulid":               IsULID,

Validators with parameters

"range(min|max)": Range,
"length(min|max)": ByteLength,
"runelength(min|max)": RuneLength,
"stringlength(min|max)": StringLength,
"matches(pattern)": StringMatches,
"in(string1|string2|...|stringN)": IsIn,
"rsapub(keylength)" : IsRsaPub,
"minstringlength(int): MinStringLength,
"maxstringlength(int): MaxStringLength,

Validators with parameters for any type

"type(type)": IsType,

And here is small example of usage:

type Post struct {
	Title    string `valid:"alphanum,required"`
	Message  string `valid:"duck,ascii"`
	Message2 string `valid:"animal(dog)"`
	AuthorIP string `valid:"ipv4"`
	Date     string `valid:"-"`
}
post := &Post{
	Title:   "My Example Post",
	Message: "duck",
	Message2: "dog",
	AuthorIP: "123.234.54.3",
}

// Add your own struct validation tags
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
	return str == "duck"
})

// Add your own struct validation tags with parameter
govalidator.ParamTagMap["animal"] = govalidator.ParamValidator(func(str string, params ...string) bool {
    species := params[0]
    return str == species
})
govalidator.ParamTagRegexMap["animal"] = regexp.MustCompile("^animal\\((\\w+)\\)$")

result, err := govalidator.ValidateStruct(post)
if err != nil {
	println("error: " + err.Error())
}
println(result)
ValidateMap #2

If you want to validate maps, you can use the map to be validated and a validation map that contain the same tags used in ValidateStruct, both maps have to be in the form map[string]interface{}

So here is small example of usage:

var mapTemplate = map[string]interface{}{
	"name":"required,alpha",
	"family":"required,alpha",
	"email":"required,email",
	"cell-phone":"numeric",
	"address":map[string]interface{}{
		"line1":"required,alphanum",
		"line2":"alphanum",
		"postal-code":"numeric",
	},
}

var inputMap = map[string]interface{}{
	"name":"Bob",
	"family":"Smith",
	"email":"[email protected]",
	"address":map[string]interface{}{
		"line1":"",
		"line2":"",
		"postal-code":"",
	},
}

result, err := govalidator.ValidateMap(inputMap, mapTemplate)
if err != nil {
	println("error: " + err.Error())
}
println(result)
WhiteList
// Remove all characters from string ignoring characters between "a" and "z"
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
Custom validation functions

Custom validation using your own domain specific validators is also available - here's an example of how to use it:

import "github.com/asaskevich/govalidator"

type CustomByteArray [6]byte // custom types are supported and can be validated

type StructWithCustomByteArray struct {
  ID              CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence
  Email           string          `valid:"email"`
  CustomMinLength int             `valid:"-"`
}

govalidator.CustomTypeTagMap.Set("customByteArrayValidator", func(i interface{}, context interface{}) bool {
  switch v := context.(type) { // you can type switch on the context interface being validated
  case StructWithCustomByteArray:
    // you can check and validate against some other field in the context,
    // return early or not validate against the context at all – your choice
  case SomeOtherType:
    // ...
  default:
    // expecting some other type? Throw/panic here or continue
  }

  switch v := i.(type) { // type switch on the struct field being validated
  case CustomByteArray:
    for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes
      if e != 0 {
        return true
      }
    }
  }
  return false
})
govalidator.CustomTypeTagMap.Set("customMinLengthValidator", func(i interface{}, context interface{}) bool {
  switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation
  case StructWithCustomByteArray:
    return len(v.ID) >= v.CustomMinLength
  }
  return false
})
Loop over Error()

By default .Error() returns all errors in a single String. To access each error you can do this:

  if err != nil {
    errs := err.(govalidator.Errors).Errors()
    for _, e := range errs {
      fmt.Println(e.Error())
    }
  }
Custom error messages

Custom error messages are supported via annotations by adding the ~ separator - here's an example of how to use it:

type Ticket struct {
  Id        int64     `json:"id"`
  FirstName string    `json:"firstname" valid:"required~First name is blank"`
}

Notes

Documentation is available here: godoc.org. Full information about code coverage is also available here: govalidator on gocover.io.

Support

If you do have a contribution to the package, feel free to create a Pull Request or an Issue.

What to contribute

If you don't know what to do, there are some features and functions that need to be done

  • Refactor code
  • Edit docs and README: spellcheck, grammar and typo check
  • Create actual list of contributors and projects that currently using this package
  • Resolve issues and bugs
  • Update actual list of functions
  • Update list of validators that available for ValidateStruct and add new
  • Implement new validators: IsFQDN, IsIMEI, IsPostalCode, IsISIN, IsISRC etc
  • Implement validation by maps
  • Implement fuzzing testing
  • Implement some struct/map/array utilities
  • Implement map/array validation
  • Implement benchmarking
  • Implement batch of examples
  • Look at forks for new features and fixes

Advice

Feel free to create what you want, but keep in mind when you implement new features:

  • Code must be clear and readable, names of variables/constants clearly describes what they are doing
  • Public functions must be documented and described in source file and added to README.md to the list of available functions
  • There are must be unit-tests for any new functions and improvements

Credits

Contributors

This project exists thanks to all the people who contribute. [Contribute].

Special thanks to contributors

Backers

Thank you to all our backers! 🙏 [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

License

FOSSA Status

govalidator's People

Contributors

andrewmunro avatar annismckenzie avatar arshiaakhavan avatar asaskevich avatar attilaolah avatar aymane-mzily avatar bboozzoo avatar blind-oracle avatar bom-d-van avatar dadie avatar daledude avatar denouche avatar eggsbenjamin avatar hvnsweeting avatar jasonlam604 avatar joostlawerman avatar kemics avatar marcsantiago avatar mie00 avatar monkeywithacupcake avatar mwmahlberg avatar nathj07 avatar nullboundary avatar rajaram-acube avatar sergeydobrodey avatar sergiosalvatore avatar sschiz avatar yezooz avatar ygj6 avatar zenovich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

govalidator's Issues

Empty string on some functions TRUE on some FALSE

On some functions empty string is valid and returns TRUE on others it is not and returns FALSE.

First of all empty string returns FALSE on IsASCII(str string)__.
On *IsAlpha(str string)
empty string returns FALSE, but on IsUTFLetter(str string) it returns TRUE

IsAlphanumeric("") returns FALSE
IsUTFLetterNumeric("") returns TRUE
IsNumeric("") returns FALSE
IsUTFNumeric("") returns TRUE
IsUTFDigit("") returns TRUE

IsLowerCase("") returns TRUE
IsUpperCase("") returns TRUE
IsFullWidth("") returns FALSE
IsHalfWidth("") returns FALSE
IsVariableWidth("") returns FALSE

As of my own opinion every character in empty string is a valid Alphanumeric, Numeric, UTFNumeric, UTFDigit, UpperCase, LowerCase, FullWidth, HalfWidth, VariableWidth, Alpha and UTFLetter character, as there is no character in empty string.

So shouldn't these functions all return TRUE on empty string?

"required" attribute non-functional for some validators

I see the unit tests do pass but they seem to avoid this case.

This prints "true" but I believe it should show an error because of the empty Title value.

package main

import "github.com/asaskevich/govalidator"

type Post struct {
    Title string `valid:"required,ascii"`
}

func main() {
    post := &Post{Title: ""}
    result, err := govalidator.ValidateStruct(post)
    if err != nil {
        println("error: " + err.Error())
    }
    println(result)
}

Option to report struct errors based on json field name

Hi,

Quite often structs are loaded from json using the standard json go functionality, i.e a struct definition below is quite common:

type User struct {
    FirstName string `json:"first_name" valid:"required"`
    LastName string `json:"first_name" valid:"required"`
}

Assuming we use the govalidator to validate our struct, it would be very handy to have the option to be able to specify that we would rather have error messages report fields names based on the json field name rather than the struct name per se.

i.e. instead of
FirstName: non zero value required
rather
first_name: non zero value required

Is this possible?

ValidateStruct not working

I have used govalidator extensively without issues. However, I have never used the ValidateStruct method, until today. For some strange reason it never validates my struct. Here is a snippet from my code:

newUser := &struct {
    Name   string `valid:"-"`
    Mobile string `valid:"numeric,required`
}{Name: "legal", Mobile: "illegal"}
result, err := govalidator.ValidateStruct(newUser)
log.Printf("result: %+v\n", result)
log.Printf("err: %+v\n", err)

result is always true and err is always nil. No matter what my struct fields contain it returns the same thing. Looking in the source I see nothing wrong with the ValidateStruct method. Someone please verify if I am doing something wrong. This has been driving me nuts all afternoon.

I am using go 1.5.1 and the latest version of govalidator from master

validateStruct - handle on struct in validation

This might be useful in a number of contexts, for example:

  1. Comparing passwords/emails in a signup form
  2. Checking unique email address in the database, where we want to exclude a user's current ID

I was thinking of something like the TagMap, e.g:

govalidators.StructMap["passwordsEqual"] = govalidators.StructValidator(func(s interface{}, str string) bool {
    return str == i.(MyStruct).PasswordCheck
})

These validators would then be run under ValidateStruct.

IsUTFDigit accepting some Strings it (IMO) shouldn't

The IsUTFDigit accepts strings which contains non valid Digit character or aren't even valid numbers.

IsUTFDigit accepts:
"-1"
"1-1"
"-"
"--------1"
"1---"
IsUTFDigits rejects:
"+1"
"1+1"
"+"
"+++++++1"
"1+++"

The question here is, what is the intended reaction of the IsUTFDigit function.

codes instead of validation messages

It's easier for localization if the error messages are codes or similar, it could be numeric codes(exporting the constants) or string codes like "email.invalid", "name.required", "password.length", etc.

Custom Validators API

Hi,

Thanks for the awesome work on the package.
I want to ask you if you'd be ok with me adding a custom validators option to the package so that other people could use the validators with this package without going to the same pains of doing the field tag reading and so on.
Thank you.

Better test pattern

The tests could be made a bit more readable. There are a few problems now:

  • When a test fails, it is not immediately clear which values caused the error.
  • It is a bit cumbersome to add new input values (due to the two slices). The two slices should be converted to a map.
  • t.Log(…) + t.FailNow() should be converted to a t.Fatal().
  • However, most errors are not fatal, so we should continue testing the rest of the values. So use t.Error() or t.Errorf() instead of t.Fatal() or t.FailNow().
  • Iterate over slices/maps instead of using the for i := … style for-loops.
  • Use parallel requests (call t.Parallel() at the top of each test case).
  • Enable the -race flag on Wercker.

As an example, here is how a test case should be changed:

Before:

func TestIsAlpha(t *testing.T) {
    tests := []string{"", "   fooo   ", "abc1", "abc", "ABC", "FoObAr"}
    expected := []bool{false, false, false, true, true, true}
    for i := 0; i < len(tests); i++ {
        result := IsAlpha(tests[i])
        if result != expected[i] {
            t.Log("Case ", i, ": expected ", expected[i], " when result is ", result)
            t.FailNow()
        }
    }
}

After:

func TestIsAlpha(t *testing.T) {
    t.Parallel()

    for input, expected := range map[string]bool{
        "":             false,
        "    foooo   ": false,
        "abc1":         false,
        "abc":          true,
        "ABC":          true,
        "FoObAr":       true,
    } {
        if result := IsAlpha(input); result != expected {
            t.Errorf("Expected IsAlpha(%q) to be %v, got %v", input, expected, result)
        }
    }
}

Test library using fuzzing

After all govalidator is kind of the frontline for any type of input and probably our tests are not perfectly checking for all possible types of input. So maybe adding some fuzzing tests could help to improve the quality of the validators.

here are two good looking fuzzing libraries:

looks complex but has many features
https://github.com/zimmski/tavor

looks simple and easy to implement.
https://github.com/google/gofuzz

Pre-compile regexes

Compile regexes at build time instead of at every function call (consts.go).

isURL not working?

I am not sure if I am doing this the right way, but

println(govalidator.IsURL(http://user@pass:domain.com/path/page))

return false for me. Shouldn't this be true? Furthermore, I get the following results:

println(govalidator.IsURL(http://www.domain.com)) returns true
println(govalidator.IsURL(http://blog.domain.com)) returns false

More Documentation / Need of Specification

Looking at issue #48, I think adding more documentation (not only in the code, but also on the README) and adding specification may helps to avoid misunderstandings on how a validation functions works/should work. A clear and good documentation and specification also may shows us cases that should be tested but currently are not. Or whether the current implementation is good or not.

The specification should be based on the idea of the validation functions and not on the current implementation. This may leads to changes in the current validation functions implementation and documentation but also leads to more reasonable and understandable validation functions.

I tried to add documentation using the information of how the validation functions works. But that only led me to some "inconsistent" behavior. E.g. in some "Is"-Numeric validation function leading zeros are valid, in some not. Sometime empty string is valid, sometimes not.

But these are not by any meaning bugs! The question is, what was the intention of the validation function (what is the specification).

I want to know more about SafeFileName function.

I want to look at the source of SafeFileName function.
but I cannot find the source in this repository.
and I tested the function in my go project. It's working.
by the way, the reason why i want to look into that function is that the function is too strict in my opinion.
What kind of rules that function followed? Can I have a reference of it?

Validating structs

Using this code from your docs:

type Post struct {
    Title    string `valid:"alphanum,required"`
    Message  string `valid:"duck,ascii"`
    AuthorIP string `valid:"ipv4"`
    Date     string `valid:"-"`
}

result, err := govalidator.ValidateStruct(post)
if err != nil {
    println("error: " + err.Error())
}
println(result)

Is there a way to detect specifically which field in the struct failed the validation test? I want to be able to return a message based on the particular field that failed validation.

IsURL marks valid URLs as invalid

It looks like some of this valid URLs marked as invalid by IsURL:

              http://www.foobar.com/~foobar
              http://foobar.com/t$-_.+!*\'(),
              http://foobar.com#baz=qux

ValidateStruct can't validate structs with embedded map[string]interface{}

type M map[string]interface{}
type T struct {
    Map M `json:"m" valid:"-"`
}

b := []byte(`{"m":{"key":"value"}}`)
var t T
if err := json.Unmarshal(b, &t); err != nil {
    // Handle unmarshal error.
}

result, err := valid.ValidateStruct(&t) // err = "function only accepts structs; got reflect.Value;"

This source of this problem is around this line:

case reflect.Map:

I think govalidator shouldn't recurse-validate into maps if valid:"-" struct tag is present or valid:"..." tag is missing.

panic with ErrorsByField

This is with go 1.5.

package main

import (
    "fmt"

    "github.com/asaskevich/govalidator"
)

type Foo struct {
    Name string `valid:"required"`
    Type string `valid:"required"`
}
type Bar struct {
    F Foo `valid:"required"`
}

func main() {
    f := Bar{Foo{Type: "Foo"}}
    result, err := govalidator.ValidateStruct(f)
    if err != nil {
        errors := govalidator.ErrorsByField(err)
        fmt.Println(errors)
    }
    println(result)

}
➜  val  go run main.go
panic: interface conversion: error is govalidator.Errors, not govalidator.Error

goroutine 1 [running]:
github.com/asaskevich/govalidator.ErrorsByField(0x708598, 0xc8201a3960, 0xc8201a3800)
    /Users/matthew/go/src/github.com/asaskevich/govalidator/validator.go:883 +0x22b
main.main()
    /Users/matthew/tmp/val/main.go:21 +0xf0

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1
exit status 2

Doesn't validate all custom validators tags

Applying multiple custom tags to a struct property only invokes the first custom validator. For example, given following scenario:

func init() {
    govalidator.CustomTypeTagMap["sqlalphanum"] = govalidator.CustomTypeValidator(func(i interface{}) bool {
        switch v := i.(type) {
        case sql.NullString:
            return govalidator.IsAlphanumeric(v.String)
        }
        return false
    })
    govalidator.CustomTypeTagMap["sqlrequired"] = govalidator.CustomTypeValidator(func(i interface{}) bool {
        switch v := i.(type) {
        case sql.NullString:
            return v.Valid
        }
        return false
    })
}

type User struct {
    Name sql.NullString `valid:"sqlrequired,sqlalphanum"`
    City sql.NullString `valid:"sqlalphanum"`
}

govalidator.ValidateStruct() on user object will invoke only sqlrequired custom validator for Name property.

Looking at the source (https://github.com/asaskevich/govalidator/blob/master/validator.go#L688), it indeed returns as soon as a custom validator is found. Shouldn't it decide validity after invoking all custom validators (if more than one)?

Return slice of errors in ValidateStruct

I think it makes more sense to return []error from govalidator.ValidateStruct since a struct can have many errors and that should be represented as a collection of errors. And it also makes my life easier because one lib I'm using expects a slice of error

lessEqual(value), moreEqual(value), equal(value), notEqual(value) Options on structs

Currently there are validator functions like IsInt, but sometime it could be even more helpful to restrict the values further with a min or/and a max value. I'd really like to extend.the functionality so that such checks could be done.

But as of now, I'm not that sure which approach would be the best, therefor I'd like to hear everyones opinion first. I'm open for other ideas or other approaches! This "issue" should be about how to solves this "feature request" the best!

As my idea goes I would like to introduce 4 new options: lessequal(value), moreequal(value), equal(value), notequal(value). I chose those options as every "validator" is in conjunction with each other. Therefor we can't say less(value) and equal(value) to get lessequal(value), because if "a < b" is true "a = b" can't be true, therefore the validation would always return false.

the usage would then look like this

type Foobar struct {
    number1 string `valid:"int,lessequal(5),moreequal(-10),notequal(0)"` //means -10 <= number1 <= 5 && number1 != 0
    cons6 string `valid:"int,equal(6)"` //means cons6 == 6
    number2 string `valid:"int,lessequal(5),notequal(5)"` //means number2 < 5
    name string `valid:"ascii,noequal(admin)"` //means every (ascii) name beside 'admin' is valid
    uuid string `valid:"uuid, moreequal(A0000000-0000-0000-0000-000000000000)"`
}

we could even add some more options like range(min,max), but at first I'd like to concentrate on the
4 options I mentioned. If this 4 are done right, adding more should be quite easy (especially options like range(min,max))

Now about how my current approach would look like respectively how I would implement this:

First of all I would define a basic Validator struct which would be more or less the foundation of most (maybe all) validators.

//Basic Validator
type basicValidatorType struct {
    less  func(v1, v2 interface{}) bool
    more  func(v1, v2 interface{}) bool
    equal func(v1, v2 interface{}) bool
}

func (b basicValidatorType) isLessEqual(value, referenceValue string) bool {
    v, errV := b.convert(value)
    r, errR := b.convert(referenceValue)
    if errV != nil || errR != nil {
        return false
    }
    if b.equal != nil && b.equal(v, r) {
        return true
    }
    if b.equal == nil && reflect.DeepEqual(v, r) {
        return true
    }
    if b.less != nil && b.less(v, r) {
        return true
    }
    return false
}
func (b basicValidatorType) isMoreEqual(value, referenceValue string) bool {
    v, errV := b.convert(value)
    r, errR := b.convert(referenceValue)
    if errV != nil || errR != nil {
        return false
    }
    if b.equal != nil && b.equal(v, r) {
        return true
    }
    if b.equal == nil && reflect.DeepEqual(v, r) {
        return true
    }
    if b.more != nil && b.more(v, r) {
        return true
    }
    return false
}
func (b basicValidatorType) isEqual(value, referenceValue string) bool {
    v, errV := b.convert(value)
    r, errR := b.convert(referenceValue)
    if errV != nil || errR != nil {
        return false
    }
    if b.equal == nil {
        return reflect.DeepEqual(v, r)
    }
    return b.equal(v, r)
}
func (b basicValidatorType) isNotEqual(value, referenceValue string) bool {
    v, errV := b.convert(value)
    r, errR := b.convert(referenceValue)
    if errV != nil || errR != nil {
        return false
    }
    if b.equal == nil {
        return !reflect.DeepEqual(v, r)
    }
    return !b.equal(v, r)
}
func (b basicValidatorType) convert(value string) (interface{}, error) {
    return value, nil
}

The validators then would look like this (here an example of how IsInt would look like)

//ValidatorType for IsInt
type validatorInt basicValidatorType

func (v validatorInt) convert(value string) (interface{}, error) {
    valueInt, err := strconv.ParseInt(value, 10, 64)
    return valueInt, err
}

var intValidator = validatorInt{
    //less
    func(v1, v2 interface{}) bool {
        int1, ok1 := v1.(int64)
        int2, ok2 := v2.(int64)
        if ok1 != true || ok2 != true {
            return false
        }
        return int1 < int2
    },
    //more
    func(v1, v2 interface{}) bool {
        int1, ok1 := v1.(int64)
        int2, ok2 := v2.(int64)
        if ok1 != true || ok2 != true {
            return false
        }
        return int1 > int2
    },
    //equal
    func(v1, v2 interface{}) bool {
        int1, ok1 := v1.(int64)
        int2, ok2 := v2.(int64)
        if ok1 != true || ok2 != true {
            return false
        }
        return int1 == int2
    },
}

func IsInt(str string) bool {
    _, err := intValidator.convert(str)
    return err == nil
}

To do all this I would need (IMO) to rebuild the whole validation process as shown in my example of IsInt. Rebuilden the whole code is something I'd like to avoid, therefor the questions are, if anyone has a better solution/approach or some ideas how to reduce code or the need to change code with my idea?

Things I currently don't like on my idea but haven't found better solutions:

  1. To much heap allocations (e.g var intValidator)

Add a way to get the tag field in validators.

I would like to use govalidator to do database validation, like checking the uniqueness.
Something like that:

govalidator.TagMap["unique"] = govalidator.Validator(func(field string, s interface{}) bool {
    return CheckUniquenessInDB(s, field)
})

Do you see a simple solution ?

Using this with XML Parsing?

If I am parsing XML I need to have the XML map on the struct.

     // Enrollment - Enrollment record
    type Enrollment struct {
        MasterEfin       string           `xml:"MasterEfin"`
        EFIN             string           `xml:"EFIN"`
        TransmitterID    string           `xml:"TransmitterId"`
        ProcessingYear   string           `xml:"ProcessingYear"`
        OfficeInfo       OfficeInfo       `xml:"OfficeInfo"`
        OwnerInformation OwnerInformation `xml:"OwnerInformation"`
        EFINOwnerInfo    EFINOwnerInfo    `xml:"EFINOwnerInfo"`
        PriorYearInfo    PriorYearInfo    `xml:"PriorYearInfo"`
        TransactionDate  string           `xml:"TransactionDate"`
    }

I'd like to read in XML and use govalidator to validate the fields before I save the data into our database.

I can't add the validation information - so do I need to move the data into a new struct to validate it? Is that the easiest method?

    type validEnrollment struct {
        MasterEfin       string           `valid:"alphanum,required"`
        EFIN             string           `valid:"alphanum,required"`
        TransmitterID    string           `valid:"alphanum,required"`
        ProcessingYear   string          `valid:"alphanum,required"`
        OfficeInfo       OfficeInfo       
        OwnerInformation OwnerInformation 
        EFINOwnerInfo    EFINOwnerInfo    
        PriorYearInfo    PriorYearInfo   
        TransactionDate  string          
    }

Suggestions? I am still learning go so be gentle. ;)

IsInt(ToString("1")) fails

tmp,_ := v.ToString(fmt.Sprint(1))
Expect(v.IsInt(tmp)).To(BeTrue(),"v.ToString makes double quotes, IsInt fails to recognize int string")

Can you add IsDomain() func ?

I think it can be usful...

I took a look in the go lib , they have such method :

func isDomainName(s string) bool {
    // See RFC 1035, RFC 3696.
    if len(s) == 0 {
        return false
    }
    if len(s) > 255 {
        return false
    }

    last := byte('.')
    ok := false // Ok once we've seen a letter.
    partlen := 0
    for i := 0; i < len(s); i++ {
        c := s[i]
        switch {
        default:
            return false
        case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_':
            ok = true
            partlen++
        case '0' <= c && c <= '9':
            // fine
            partlen++
        case c == '-':
            // Byte before dash cannot be dot.
            if last == '.' {
                return false
            }
            partlen++
        case c == '.':
            // Byte before dot cannot be dot, dash.
            if last == '.' || last == '-' {
                return false
            }
            if partlen > 63 || partlen == 0 {
                return false
            }
            partlen = 0
        }
        last = c
    }
    if last == '-' || partlen > 63 {
        return false
    }

    return ok
}

Problem validating a nested struct

Hi,

I have a case where govalidator does not check a tag on of a nested struct:

screen shot 2016-02-16 at 2 19 50 pm

where Owner ID of both structs (top in Notification and the one under Profile) is declared as:

OwnerID             string              `bson:"owner_id" json:"owner_id" valid:"uuid, length(36|36)"`

I.e. this test would fail.

Also,

I noticed validation is not working on private properties.

For example, this one will work

pInv := Profile{
        OwnerID: "invalid uuid",
    }

while with a private ownerID property, won't:

pInv := Profile{
        ownerID: "invalid uuid",
    }

Thank you
D.

JSON style validation errors

It would be neat to have the errors returned as JSON instead of a string.

Given

type Profile struct {
  Email string `json:"email" valid:”required,email”`
}

type User struct {
  Profile Profile `json:"profile" valid:”…”`
}

you get errors back like

{
  “profile”: {
    “email”: [
      “non zero value required",
      "does not validate as email"
    ]
  }
}

Can't Validate Type Int

It appears int is no longer a valid struct validator. I'm getting a message "Field: Validator int doesn't supported Kind int", which is strange.

You can reproduce by adding ,int to line 1572 of validator_test.go.

I could be way off, too, let me know if you want a more proper test case or to dig in further.

add validate file path function

  • should validate if string is a valid file path
  • should differ between OSes e.g. /home/me/file.txt is valid in unix based OSes.
  • anything else I haven't thought of

Validator int doesn't supported Kind int

Hello,

I recently updated your package and I am getting an strange issue !
I get this :

Limit: Validator int doesn't supported Kind int;

When I try to validate this struct :

var Form struct {
    Offset int `valid:"int"`
    Limit  int `valid:"int"`
}

And If I remove Limit Validator the issue disappear.

Thanks !

How to list errors by field

I am wondering how to get errors for specific fields for the purpose of showing invalid input on a form. As far as I can gather ValidateStruct just returns a normal error object that is a string of all of the errors. Is there a way to get the errors for a specific field or am I missing something.

Thanks for any help.

Can't validate Wikipedia's URL

Hi. Thanks for this awesome project!

I just notice that a home page URL of Wikipedia (which contains Unicode characters) can not pass the validation: https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5

Tests fail sometimes

The tests seem to depend on a global state. They sometimes fail with the following error message:

--- FAIL: TestEscape (0.00 seconds)
        utils_test.go:155: Case  0 : expected  &lt;img alt=&quot;foo&amp;bar&quot;&gt;  when result is  &amp;lt;img alt=&amp;quot;foo&amp;bar&amp;quot;&amp;gt;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.