An idiomatic Go (golang) validation package. Supports configurable and extensible validation rules (validators) using normal language constructs instead of error-prone struct tags.

Overview

ozzo-validation

GoDoc Build Status Coverage Status Go Report

Description

ozzo-validation is a Go package that provides configurable and extensible data validation capabilities. It has the following features:

  • use normal programming constructs rather than error-prone struct tags to specify how data should be validated.
  • can validate data of different types, e.g., structs, strings, byte slices, slices, maps, arrays.
  • can validate custom data types as long as they implement the Validatable interface.
  • can validate data types that implement the sql.Valuer interface (e.g. sql.NullString).
  • customizable and well-formatted validation errors.
  • error code and message translation support.
  • provide a rich set of validation rules right out of box.
  • extremely easy to create and use custom validation rules.

For an example on how this library is used in an application, please refer to go-rest-api which is a starter kit for building RESTful APIs in Go.

Requirements

Go 1.13 or above.

Getting Started

The ozzo-validation package mainly includes a set of validation rules and two validation methods. You use validation rules to describe how a value should be considered valid, and you call either validation.Validate() or validation.ValidateStruct() to validate the value.

Installation

Run the following command to install the package:

go get github.com/go-ozzo/ozzo-validation

Validating a Simple Value

For a simple value, such as a string or an integer, you may use validation.Validate() to validate it. For example,

package main

import (
	"fmt"

	"github.com/go-ozzo/ozzo-validation/v4"
	"github.com/go-ozzo/ozzo-validation/v4/is"
)

func main() {
	data := "example"
	err := validation.Validate(data,
		validation.Required,       // not empty
		validation.Length(5, 100), // length between 5 and 100
		is.URL,                    // is a valid URL
	)
	fmt.Println(err)
	// Output:
	// must be a valid URL
}

The method validation.Validate() will run through the rules in the order that they are listed. If a rule fails the validation, the method will return the corresponding error and skip the rest of the rules. The method will return nil if the value passes all validation rules.

Validating a Struct

For a struct value, you usually want to check if its fields are valid. For example, in a RESTful application, you may unmarshal the request payload into a struct and then validate the struct fields. If one or multiple fields are invalid, you may want to get an error describing which fields are invalid. You can use validation.ValidateStruct() to achieve this purpose. A single struct can have rules for multiple fields, and a field can be associated with multiple rules. For example,

type Address struct {
	Street string
	City   string
	State  string
	Zip    string
}

func (a Address) Validate() error {
	return validation.ValidateStruct(&a,
		// Street cannot be empty, and the length must between 5 and 50
		validation.Field(&a.Street, validation.Required, validation.Length(5, 50)),
		// City cannot be empty, and the length must between 5 and 50
		validation.Field(&a.City, validation.Required, validation.Length(5, 50)),
		// State cannot be empty, and must be a string consisting of two letters in upper case
		validation.Field(&a.State, validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
		// State cannot be empty, and must be a string consisting of five digits
		validation.Field(&a.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
	)
}

a := Address{
    Street: "123",
    City:   "Unknown",
    State:  "Virginia",
    Zip:    "12345",
}

err := a.Validate()
fmt.Println(err)
// Output:
// Street: the length must be between 5 and 50; State: must be in a valid format.

Note that when calling validation.ValidateStruct to validate a struct, you should pass to the method a pointer to the struct instead of the struct itself. Similarly, when calling validation.Field to specify the rules for a struct field, you should use a pointer to the struct field.

When the struct validation is performed, the fields are validated in the order they are specified in ValidateStruct. And when each field is validated, its rules are also evaluated in the order they are associated with the field. If a rule fails, an error is recorded for that field, and the validation will continue with the next field.

Validating a Map

Sometimes you might need to work with dynamic data stored in maps rather than a typed model. You can use validation.Map() in this situation. A single map can have rules for multiple keys, and a key can be associated with multiple rules. For example,

c := map[string]interface{}{
	"Name":  "Qiang Xue",
	"Email": "q",
	"Address": map[string]interface{}{
		"Street": "123",
		"City":   "Unknown",
		"State":  "Virginia",
		"Zip":    "12345",
	},
}

err := validation.Validate(c,
	validation.Map(
		// Name cannot be empty, and the length must be between 5 and 20.
		validation.Key("Name", validation.Required, validation.Length(5, 20)),
		// Email cannot be empty and should be in a valid email format.
		validation.Key("Email", validation.Required, is.Email),
		// Validate Address using its own validation rules
		validation.Key("Address", validation.Map(
			// Street cannot be empty, and the length must between 5 and 50
			validation.Key("Street", validation.Required, validation.Length(5, 50)),
			// City cannot be empty, and the length must between 5 and 50
			validation.Key("City", validation.Required, validation.Length(5, 50)),
			// State cannot be empty, and must be a string consisting of two letters in upper case
			validation.Key("State", validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
			// State cannot be empty, and must be a string consisting of five digits
			validation.Key("Zip", validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
		)),
	),
)
fmt.Println(err)
// Output:
// Address: (State: must be in a valid format; Street: the length must be between 5 and 50.); Email: must be a valid email address.

When the map validation is performed, the keys are validated in the order they are specified in Map. And when each key is validated, its rules are also evaluated in the order they are associated with the key. If a rule fails, an error is recorded for that key, and the validation will continue with the next key.

Validation Errors

The validation.ValidateStruct method returns validation errors found in struct fields in terms of validation.Errors which is a map of fields and their corresponding errors. Nil is returned if validation passes.

By default, validation.Errors uses the struct tags named json to determine what names should be used to represent the invalid fields. The type also implements the json.Marshaler interface so that it can be marshaled into a proper JSON object. For example,

type Address struct {
	Street string `json:"street"`
	City   string `json:"city"`
	State  string `json:"state"`
	Zip    string `json:"zip"`
}

// ...perform validation here...

err := a.Validate()
b, _ := json.Marshal(err)
fmt.Println(string(b))
// Output:
// {"street":"the length must be between 5 and 50","state":"must be in a valid format"}

You may modify validation.ErrorTag to use a different struct tag name.

If you do not like the magic that ValidateStruct determines error keys based on struct field names or corresponding tag values, you may use the following alternative approach:

c := Customer{
	Name:  "Qiang Xue",
	Email: "q",
	Address: Address{
		State:  "Virginia",
	},
}

err := validation.Errors{
	"name": validation.Validate(c.Name, validation.Required, validation.Length(5, 20)),
	"email": validation.Validate(c.Name, validation.Required, is.Email),
	"zip": validation.Validate(c.Address.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
}.Filter()
fmt.Println(err)
// Output:
// email: must be a valid email address; zip: cannot be blank.

In the above example, we build a validation.Errors by a list of names and the corresponding validation results. At the end we call Errors.Filter() to remove from Errors all nils which correspond to those successful validation results. The method will return nil if Errors is empty.

The above approach is very flexible as it allows you to freely build up your validation error structure. You can use it to validate both struct and non-struct values. Compared to using ValidateStruct to validate a struct, it has the drawback that you have to redundantly specify the error keys while ValidateStruct can automatically find them out.

Internal Errors

Internal errors are different from validation errors in that internal errors are caused by malfunctioning code (e.g. a validator making a remote call to validate some data when the remote service is down) rather than the data being validated. When an internal error happens during data validation, you may allow the user to resubmit the same data to perform validation again, hoping the program resumes functioning. On the other hand, if data validation fails due to data error, the user should generally not resubmit the same data again.

To differentiate internal errors from validation errors, when an internal error occurs in a validator, wrap it into validation.InternalError by calling validation.NewInternalError(). The user of the validator can then check if a returned error is an internal error or not. For example,

if err := a.Validate(); err != nil {
	if e, ok := err.(validation.InternalError); ok {
		// an internal error happened
		fmt.Println(e.InternalError())
	}
}

Validatable Types

A type is validatable if it implements the validation.Validatable interface.

When validation.Validate is used to validate a validatable value, if it does not find any error with the given validation rules, it will further call the value's Validate() method.

Similarly, when validation.ValidateStruct is validating a struct field whose type is validatable, it will call the field's Validate method after it passes the listed rules.

Note: When implementing validation.Validatable, do not call validation.Validate() to validate the value in its original type because this will cause infinite loops. For example, if you define a new type MyString as string and implement validation.Validatable for MyString, within the Validate() function you should cast the value to string first before calling validation.Validate() to validate it.

In the following example, the Address field of Customer is validatable because Address implements validation.Validatable. Therefore, when validating a Customer struct with validation.ValidateStruct, validation will "dive" into the Address field.

type Customer struct {
	Name    string
	Gender  string
	Email   string
	Address Address
}

func (c Customer) Validate() error {
	return validation.ValidateStruct(&c,
		// Name cannot be empty, and the length must be between 5 and 20.
		validation.Field(&c.Name, validation.Required, validation.Length(5, 20)),
		// Gender is optional, and should be either "Female" or "Male".
		validation.Field(&c.Gender, validation.In("Female", "Male")),
		// Email cannot be empty and should be in a valid email format.
		validation.Field(&c.Email, validation.Required, is.Email),
		// Validate Address using its own validation rules
		validation.Field(&c.Address),
	)
}

c := Customer{
	Name:  "Qiang Xue",
	Email: "q",
	Address: Address{
		Street: "123 Main Street",
		City:   "Unknown",
		State:  "Virginia",
		Zip:    "12345",
	},
}

err := c.Validate()
fmt.Println(err)
// Output:
// Address: (State: must be in a valid format.); Email: must be a valid email address.

Sometimes, you may want to skip the invocation of a type's Validate method. To do so, simply associate a validation.Skip rule with the value being validated.

Maps/Slices/Arrays of Validatables

When validating an iterable (map, slice, or array), whose element type implements the validation.Validatable interface, the validation.Validate method will call the Validate method of every non-nil element. The validation errors of the elements will be returned as validation.Errors which maps the keys of the invalid elements to their corresponding validation errors. For example,

addresses := []Address{
	Address{State: "MD", Zip: "12345"},
	Address{Street: "123 Main St", City: "Vienna", State: "VA", Zip: "12345"},
	Address{City: "Unknown", State: "NC", Zip: "123"},
}
err := validation.Validate(addresses)
fmt.Println(err)
// Output:
// 0: (City: cannot be blank; Street: cannot be blank.); 2: (Street: cannot be blank; Zip: must be in a valid format.).

When using validation.ValidateStruct to validate a struct, the above validation procedure also applies to those struct fields which are map/slices/arrays of validatables.

Each

The Each validation rule allows you to apply a set of rules to each element of an array, slice, or map.

type Customer struct {
    Name      string
    Emails    []string
}

func (c Customer) Validate() error {
    return validation.ValidateStruct(&c,
        // Name cannot be empty, and the length must be between 5 and 20.
		validation.Field(&c.Name, validation.Required, validation.Length(5, 20)),
		// Emails are optional, but if given must be valid.
		validation.Field(&c.Emails, validation.Each(is.Email)),
    )
}

c := Customer{
    Name:   "Qiang Xue",
    Emails: []Email{
        "[email protected]",
        "invalid",
    },
}

err := c.Validate()
fmt.Println(err)
// Output:
// Emails: (1: must be a valid email address.).

Pointers

When a value being validated is a pointer, most validation rules will validate the actual value pointed to by the pointer. If the pointer is nil, these rules will skip the validation.

An exception is the validation.Required and validation.NotNil rules. When a pointer is nil, they will report a validation error.

Types Implementing sql.Valuer

If a data type implements the sql.Valuer interface (e.g. sql.NullString), the built-in validation rules will handle it properly. In particular, when a rule is validating such data, it will call the Value() method and validate the returned value instead.

Required vs. Not Nil

When validating input values, there are two different scenarios about checking if input values are provided or not.

In the first scenario, an input value is considered missing if it is not entered or it is entered as a zero value (e.g. an empty string, a zero integer). You can use the validation.Required rule in this case.

In the second scenario, an input value is considered missing only if it is not entered. A pointer field is usually used in this case so that you can detect if a value is entered or not by checking if the pointer is nil or not. You can use the validation.NotNil rule to ensure a value is entered (even if it is a zero value).

Embedded Structs

The validation.ValidateStruct method will properly validate a struct that contains embedded structs. In particular, the fields of an embedded struct are treated as if they belong directly to the containing struct. For example,

type Employee struct {
	Name string
}

type Manager struct {
	Employee
	Level int
}

m := Manager{}
err := validation.ValidateStruct(&m,
	validation.Field(&m.Name, validation.Required),
	validation.Field(&m.Level, validation.Required),
)
fmt.Println(err)
// Output:
// Level: cannot be blank; Name: cannot be blank.

In the above code, we use &m.Name to specify the validation of the Name field of the embedded struct Employee. And the validation error uses Name as the key for the error associated with the Name field as if Name a field directly belonging to Manager.

If Employee implements the validation.Validatable interface, we can also use the following code to validate Manager, which generates the same validation result:

func (e Employee) Validate() error {
	return validation.ValidateStruct(&e,
		validation.Field(&e.Name, validation.Required),
	)
}

err := validation.ValidateStruct(&m,
	validation.Field(&m.Employee),
	validation.Field(&m.Level, validation.Required),
)
fmt.Println(err)
// Output:
// Level: cannot be blank; Name: cannot be blank.

Conditional Validation

Sometimes, we may want to validate a value only when certain condition is met. For example, we want to ensure the unit struct field is not empty only when the quantity field is not empty; or we may want to ensure either email or phone is provided. The so-called conditional validation can be achieved with the help of validation.When. The following code implements the aforementioned examples:

result := validation.ValidateStruct(&a,
    validation.Field(&a.Unit, validation.When(a.Quantity != "", validation.Required).Else(validation.Nil)),
    validation.Field(&a.Phone, validation.When(a.Email == "", validation.Required.Error('Either phone or Email is required.')),
    validation.Field(&a.Email, validation.When(a.Phone == "", validation.Required.Error('Either phone or Email is required.')),
)

Note that validation.When and validation.When.Else can take a list of validation rules. These rules will be executed only when the condition is true (When) or false (Else).

The above code can also be simplified using the shortcut validation.Required.When:

result := validation.ValidateStruct(&a,
    validation.Field(&a.Unit, validation.Required.When(a.Quantity != ""), validation.Nil.When(a.Quantity == "")),
    validation.Field(&a.Phone, validation.Required.When(a.Email == "").Error('Either phone or Email is required.')),
    validation.Field(&a.Email, validation.Required.When(a.Phone == "").Error('Either phone or Email is required.')),
)

Customizing Error Messages

All built-in validation rules allow you to customize their error messages. To do so, simply call the Error() method of the rules. For example,

data := "2123"
err := validation.Validate(data,
	validation.Required.Error("is required"),
	validation.Match(regexp.MustCompile("^[0-9]{5}$")).Error("must be a string with five digits"),
)
fmt.Println(err)
// Output:
// must be a string with five digits

You can also customize the pre-defined error(s) of a built-in rule such that the customization applies to every instance of the rule. For example, the Required rule uses the pre-defined error ErrRequired. You can customize it during the application initialization:

validation.ErrRequired = validation.ErrRequired.SetMessage("the value is required") 

Error Code and Message Translation

The errors returned by the validation rules implement the Error interface which contains the Code() method to provide the error code information. While the message of a validation error is often customized, the code is immutable. You can use error code to programmatically check a validation error or look for the translation of the corresponding message.

If you are developing your own validation rules, you can use validation.NewError() to create a validation error which implements the aforementioned Error interface.

Creating Custom Rules

Creating a custom rule is as simple as implementing the validation.Rule interface. The interface contains a single method as shown below, which should validate the value and return the validation error, if any:

// Validate validates a value and returns an error if validation fails.
Validate(value interface{}) error

If you already have a function with the same signature as shown above, you can call validation.By() to turn it into a validation rule. For example,

func checkAbc(value interface{}) error {
	s, _ := value.(string)
	if s != "abc" {
		return errors.New("must be abc")
	}
	return nil
}

err := validation.Validate("xyz", validation.By(checkAbc))
fmt.Println(err)
// Output: must be abc

If your validation function takes additional parameters, you can use the following closure trick:

func stringEquals(str string) validation.RuleFunc {
	return func(value interface{}) error {
		s, _ := value.(string)
        if s != str {
            return errors.New("unexpected string")
        }
        return nil
    }
}

err := validation.Validate("xyz", validation.By(stringEquals("abc")))
fmt.Println(err)
// Output: unexpected string

Rule Groups

When a combination of several rules are used in multiple places, you may use the following trick to create a rule group so that your code is more maintainable.

var NameRule = []validation.Rule{
	validation.Required,
	validation.Length(5, 20),
}

type User struct {
	FirstName string
	LastName  string
}

func (u User) Validate() error {
	return validation.ValidateStruct(&u,
		validation.Field(&u.FirstName, NameRule...),
		validation.Field(&u.LastName, NameRule...),
	)
}

In the above example, we create a rule group NameRule which consists of two validation rules. We then use this rule group to validate both FirstName and LastName.

Context-aware Validation

While most validation rules are self-contained, some rules may depend dynamically on a context. A rule may implement the validation.RuleWithContext interface to support the so-called context-aware validation.

To validate an arbitrary value with a context, call validation.ValidateWithContext(). The context.Conext parameter will be passed along to those rules that implement validation.RuleWithContext.

To validate the fields of a struct with a context, call validation.ValidateStructWithContext().

You can define a context-aware rule from scratch by implementing both validation.Rule and validation.RuleWithContext. You can also use validation.WithContext() to turn a function into a context-aware rule. For example,

rule := validation.WithContext(func(ctx context.Context, value interface{}) error {
	if ctx.Value("secret") == value.(string) {
	    return nil
	}
	return errors.New("value incorrect")
})
value := "xyz"
ctx := context.WithValue(context.Background(), "secret", "example")
err := validation.ValidateWithContext(ctx, value, rule)
fmt.Println(err)
// Output: value incorrect

When performing context-aware validation, if a rule does not implement validation.RuleWithContext, its validation.Rule will be used instead.

Built-in Validation Rules

The following rules are provided in the validation package:

  • In(...interface{}): checks if a value can be found in the given list of values.
  • NotIn(...interface{}): checks if a value is NOT among the given list of values.
  • Length(min, max int): checks if the length of a value is within the specified range. This rule should only be used for validating strings, slices, maps, and arrays.
  • RuneLength(min, max int): checks if the length of a string is within the specified range. This rule is similar as Length except that when the value being validated is a string, it checks its rune length instead of byte length.
  • Min(min interface{}) and Max(max interface{}): checks if a value is within the specified range. These two rules should only be used for validating int, uint, float and time.Time types.
  • Match(*regexp.Regexp): checks if a value matches the specified regular expression. This rule should only be used for strings and byte slices.
  • Date(layout string): checks if a string value is a date whose format is specified by the layout. By calling Min() and/or Max(), you can check additionally if the date is within the specified range.
  • Required: checks if a value is not empty (neither nil nor zero).
  • NotNil: checks if a pointer value is not nil. Non-pointer values are considered valid.
  • NilOrNotEmpty: checks if a value is a nil pointer or a non-empty value. This differs from Required in that it treats a nil pointer as valid.
  • Nil: checks if a value is a nil pointer.
  • Empty: checks if a value is empty. nil pointers are considered valid.
  • Skip: this is a special rule used to indicate that all rules following it should be skipped (including the nested ones).
  • MultipleOf: checks if the value is a multiple of the specified range.
  • Each(rules ...Rule): checks the elements within an iterable (map/slice/array) with other rules.
  • When(condition, rules ...Rule): validates with the specified rules only when the condition is true.
  • Else(rules ...Rule): must be used with When(condition, rules ...Rule), validates with the specified rules only when the condition is false.

The is sub-package provides a list of commonly used string validation rules that can be used to check if the format of a value satisfies certain requirements. Note that these rules only handle strings and byte slices and if a string or byte slice is empty, it is considered valid. You may use a Required rule to ensure a value is not empty. Below is the whole list of the rules provided by the is package:

  • Email: validates if a string is an email or not. It also checks if the MX record exists for the email domain.
  • EmailFormat: validates if a string is an email or not. It does NOT check the existence of the MX record.
  • URL: validates if a string is a valid URL
  • RequestURL: validates if a string is a valid request URL
  • RequestURI: validates if a string is a valid request URI
  • Alpha: validates if a string contains English letters only (a-zA-Z)
  • Digit: validates if a string contains digits only (0-9)
  • Alphanumeric: validates if a string contains English letters and digits only (a-zA-Z0-9)
  • UTFLetter: validates if a string contains unicode letters only
  • UTFDigit: validates if a string contains unicode decimal digits only
  • UTFLetterNumeric: validates if a string contains unicode letters and numbers only
  • UTFNumeric: validates if a string contains unicode number characters (category N) only
  • LowerCase: validates if a string contains lower case unicode letters only
  • UpperCase: validates if a string contains upper case unicode letters only
  • Hexadecimal: validates if a string is a valid hexadecimal number
  • HexColor: validates if a string is a valid hexadecimal color code
  • RGBColor: validates if a string is a valid RGB color in the form of rgb(R, G, B)
  • Int: validates if a string is a valid integer number
  • Float: validates if a string is a floating point number
  • UUIDv3: validates if a string is a valid version 3 UUID
  • UUIDv4: validates if a string is a valid version 4 UUID
  • UUIDv5: validates if a string is a valid version 5 UUID
  • UUID: validates if a string is a valid UUID
  • CreditCard: validates if a string is a valid credit card number
  • ISBN10: validates if a string is an ISBN version 10
  • ISBN13: validates if a string is an ISBN version 13
  • ISBN: validates if a string is an ISBN (either version 10 or 13)
  • JSON: validates if a string is in valid JSON format
  • ASCII: validates if a string contains ASCII characters only
  • PrintableASCII: validates if a string contains printable ASCII characters only
  • Multibyte: validates if a string contains multibyte characters
  • FullWidth: validates if a string contains full-width characters
  • HalfWidth: validates if a string contains half-width characters
  • VariableWidth: validates if a string contains both full-width and half-width characters
  • Base64: validates if a string is encoded in Base64
  • DataURI: validates if a string is a valid base64-encoded data URI
  • E164: validates if a string is a valid E164 phone number (+19251232233)
  • CountryCode2: validates if a string is a valid ISO3166 Alpha 2 country code
  • CountryCode3: validates if a string is a valid ISO3166 Alpha 3 country code
  • DialString: validates if a string is a valid dial string that can be passed to Dial()
  • MAC: validates if a string is a MAC address
  • IP: validates if a string is a valid IP address (either version 4 or 6)
  • IPv4: validates if a string is a valid version 4 IP address
  • IPv6: validates if a string is a valid version 6 IP address
  • Subdomain: validates if a string is valid subdomain
  • Domain: validates if a string is valid domain
  • DNSName: validates if a string is valid DNS name
  • Host: validates if a string is a valid IP (both v4 and v6) or a valid DNS name
  • Port: validates if a string is a valid port number
  • MongoID: validates if a string is a valid Mongo ID
  • Latitude: validates if a string is a valid latitude
  • Longitude: validates if a string is a valid longitude
  • SSN: validates if a string is a social security number (SSN)
  • Semver: validates if a string is a valid semantic version

Credits

The is sub-package wraps the excellent validators provided by the govalidator package.

Issues
  • Suggestion for library improvement

    Suggestion for library improvement

    This library is great. I had been looking for a library that would be as elegant and flexible as the NodeJS Joi library developed by Walmart for their hapijs framework and I found this library that was the closest thing to the Joi library. I would like to make the following suggestions to improve the library.

    • Field names passed in to the validator should be pointer based instead of a string. For example, use &c.Name instead of "Name". This would move the error for using the wrong field name from a runtime error to a compile time error.
    • Incorporate the validation rules from the goburrow validator into the go-ozzo validator which have been really thought out and powerful. For example goburrow has the "min" validation rule, which applies the rule depending upon what has been passed in. If a string is passed in, then it checks for the minimum length. If a slice has been passed in, then it checks for the length of the array. If an integer has been passed in, then it checks if the value is equal to or greater than the min value. And so on...

    Thanks for a great library!

    opened by msaron 19
  • Add translation feature.

    Add translation feature.

    Hi, I added the translation feature and also modified all the rules to use this feature.

    I assume each rule's message property is the custom message, so any user sets it, that rule returns the user's message, otherwise, return the translated message.

    Each rule can return the translated message if it does not exists return the English message, and if the English message does not exist for that rule, return rule's default message.

    All functions work just like before Except one function:
    I Changed NewStringRule function signature from
    func validation.NewStringRule(validation stringValidator,message string) to
    func validation.NewStringRule(validation stringValidator,ruleName string)
    this is because we don't need the message here anymore, just need to get a name for that rule and get the rule's message from the translation map.

    I also changed your package version from 3 to 4 because of NewStringRule function's signature changes.

    opened by mehran-prs 12
  • cannot find package

    cannot find package "github.com/go-ozzo/ozzo-validation/v4"

    Hi,

    I am trying to install the package and getting error when i run the following command go get github.com/go-ozzo/ozzo-validation/v4

    ERROR

    Lucians-MacBook-Pro:jeoga_go khanakia$ go get github.com/go-ozzo/ozzo-validation/v4
    package github.com/go-ozzo/ozzo-validation/v4: cannot find package "github.com/go-ozzo/ozzo-validation/v4" in any of:
    	/usr/local/Cellar/go/1.14.2_1/libexec/src/github.com/go-ozzo/ozzo-validation/v4 (from $GOROOT)
    	/Volumes/D/www/go/src/github.com/go-ozzo/ozzo-validation/v4 (from $GOPATH)
    	/Volumes/D/www/go/src/github.com/khanakia/go_code/jeoga_go/src/github.com/go-ozzo/ozzo-validation/v4
    Lucians-MacBook-Pro:jeoga_go khanakia$ 
    
    opened by khanakia 12
  • Validate untyped maps similar to structs

    Validate untyped maps similar to structs

    As discussed in #126, I decided to give it a shot including @dlpetrie's suggestion for maps within structs (and visa versa).

    This PR also includes a non-breaking change to allow non-pointer structs in ValidateStruct plus some minor linting tweaks. I can split these into separate PRs if preferred.

    opened by NathanBaulch 9
  • fix(README): update GoDoc link to v4's

    fix(README): update GoDoc link to v4's

    As the title states, the change is so tiny

    opened by mrg0lden 9
  • Support validating embedded structs

    Support validating embedded structs

    Considering the following:

    type ListingOptions struct {
    	Offset    int    `json:"offset"`
    	Limit     int    `json:"limit"`
    	OrderBy   string `json:"order_by"`
    	OrderDesc bool   `json:"order_desc"`
    }
    
    func (l ListingOptions) Validate() error {
    	return validation.ValidateStruct(&l,
    		// Offset should be a positive number
    		validation.Field(&l.Offset, validation.Min(0)),
    		// Limit should be between 10 and 50
    		validation.Field(&l.Limit, validation.Min(10), validation.Max(50)),
    	)
    }
    
    type BookListingOptions struct {
    	UserID   int             `json:"user_id"`
    	Status   []string       `json:"status"`
    
    	ListingOptions
    }
    
    func (l BookListingOptions) Validate() error {
    	validOrderColumns := []string{
    		"name", "status", "created_at", "updated_at",
    	}
    
    	return validation.ValidateStruct(&l,
    		// UserID should be a positive number
    		validation.Field(&l.UserID, validation.Min(0)),
    		// Status should be a valid book status
    		validation.Field(&l.Status, validation.In("open", "closed")),
    		// ListingOptions.Order should be a valid order clause
    		validation.Field(&l.ListingOptions.OrderBy, validation.In(validOrderColumns)),
    		// ListingOptions has its own validation
    		validation.Field(&l.ListingOptions),
    	)
    }
    

    Calling BookListingOptions{}.Validate() returns the following error: field #2 cannot be found in the struct

    opened by noonien 8
  • Add Each() method for validating iterables.

    Add Each() method for validating iterables.

    I was really missing the ability to validate on arrays or maps, so I've added the Each() method that was mentioned in https://github.com/go-ozzo/ozzo-validation/issues/55.

    I've tried to test is as thoroughly as possible.

    opened by petervandenbroek 7
  • crossfield struct validation

    crossfield struct validation

    Hello!

    There doesn't seem to be a way to do cross field validation for a struct.

    Given:

    type Bucket struct {
      Accept []string
      Reject []string
    }
    

    How can I write Bucket.Validate with a rule that checks that Accept and Reject are mutually exclusive?

    func (b Bucket) Validate() error {
    return validation.Validate(&b, validation.By(bucketMutualExclusionCheck)
    }
    

    cause an stack overflow because validation.Validate calls Validate on Bucket and so forth... infinite recursion.

    The other solution also seems like a hack, because it errors on one or the other field instead of the whole struct:

    func (b Bucket) Validate() error {
      return validation.ValidateStruct(&b,
        validation.Field(&b.Accept, validation.By(sliceMutualExclusionCheck(b.Reject))),
      )
    }
    

    Is there a better way to tackle this kind of validations?

    opened by omeid 7
  • Validate if not nil

    Validate if not nil

    Considering the following type:

    type Foo struct {
        OptionalBar *string
    }
    

    Would it be possible to validate OptionalBar only if it's not nil?

    opened by noonien 6
  • add optional key, dynamic map; update ReadMe

    add optional key, dynamic map; update ReadMe

    • adding function to create validation.Key().Optional() to improve readability
    • adding function to create validation.Map().AllowExtraKeys() to improve readability
    • updating readme related to validation.Map(), add new section about allowing extra keys and optional keys
    opened by Jessinra 1
  • Better Docs for translation section.

    Better Docs for translation section.

    can you make an example folder and put some translation samples in it. or maybe make a better document for newbies to understand how to add translations.

    opened by mhosseintaher 0
  • Ignoring extra keys in Validate.Map()

    Ignoring extra keys in Validate.Map()

    Hey,

    I am trying to write a validation function where I am okay with having extra keys in my map as long as the original sets of keys are provided,

    Eg,

    data := map[string]interface{}{
    		"Name":      "XYZ",
    		"Email":    "[email protected]",
    		"Password": "abc123"
    	}
    
    
    v := validation.Validate(data,
    		validation.Map(
    			validation.Key("Name", validation.Required),
    			validation.Key("Email", validation.Required),
    			validation.Key("Password", validation.Required),
                            //skip any other extra keys
    
    		),
    	)
    
    
    

    I am okay if my map has extra keys other than these. I am currently iterating over the Errors struct and manually checking the error message. Is there a cleaner way to do it?

    opened by joshivaibhav 2
  • Nil pointer dereference with Errors.Error()

    Nil pointer dereference with Errors.Error()

    Hi,

    Thank You for this library, it is amazing.

    I have found a bug, here is the reproducer:

    package main
    
    import (
    	validation "github.com/go-ozzo/ozzo-validation/v4"
    )
    
    type User struct {
    	Login string
    }
    
    func main() {
    	u := User{
    		Login: "blah",
    	}
    
    
    	u.Validate()
    }
    
    func (u User) Validate() error {
    	Err := validation.Errors{
    		"Login": validation.Validate(u.Login, validation.Required),
    	}
    	_ = Err.Error()
    	return Err.Filter()
    }
    

    Calling Err.Error() cases panic because of a nil pointer dereference.

    Below the suggested fix:

    From 34a1f17ee23bb604c93a5d67aa83581f837f2d5a Mon Sep 17 00:00:00 2001
    From: =?UTF-8?q?V=C3=96R=C3=96SK=C5=90I=20Andr=C3=A1s?= <[email protected]>
    Date: Wed, 21 Apr 2021 22:15:41 +0200
    Subject: [PATCH] Fix nil pointer reference in Errors.Error() method
    
    ---
     error.go | 3 +++
     1 file changed, 3 insertions(+)
    
    diff --git a/error.go b/error.go
    index e75b1de..4284f51 100644
    --- a/error.go
    +++ b/error.go
    @@ -128,6 +128,9 @@ func (es Errors) Error() string {
     
     	var s strings.Builder
     	for i, key := range keys {
    +		if es[key] == nil {
    +			continue
    +		}
     		if i > 0 {
     			s.WriteString("; ")
     		}
    -- 
    2.29.2.windows.3
    

    Thank You!

    opened by voroskoi 0
  • add int rule

    add int rule

    As per some pr's that have been sitting around for a while (e.g. #93) and uncertainty as to how this framework is used in the larger ozzo project I wanted to make sure that this repo was still open before I spend too much time on this.

    I've taken the int_rule functionality (and corresponding test case) I added in ozzo-validators and added it here. This allows you to validate structs on non-string fields.

    For instance, IsValidPort and IsAvailablePort here validates on an int rather than a string.

    Here's what that looks like in the context of a struct validation:

    image

    opened by jakesyl 1
  • isEmpty can returns true, if an argument is any struct other than time.Time

    isEmpty can returns true, if an argument is any struct other than time.Time

    resolve https://github.com/go-ozzo/ozzo-validation/issues/143

    Please see the issue for details.

    opened by soranoba 1
  • isEmpty always returns false, if an argument is any struct other than time.Time

    isEmpty always returns false, if an argument is any struct other than time.Time

    ref:

    • https://github.com/go-ozzo/ozzo-validation/blob/34bd5476bd5bb4884aee8252974da4cd4e878a75/util_test.go#L253-L255
    • https://github.com/go-ozzo/ozzo-validation/blob/34bd5476bd5bb4884aee8252974da4cd4e878a75/util.go#L119-L124

    Do you need to treat specially time.Time? I want all structs to be treated the same.

    opened by soranoba 0
  • showing '-' as field name in error for hidden fields

    showing '-' as field name in error for hidden fields

    I'm validating a User struct whose password field is not exposed to the client. This is my user struct

    type User struct {
    	Name      string              `json:"name" bson:"name"`
    	Email     string              `json:"email" bson:"email"`
    	Password  string              `json:"-" bson:"password"`
    }
    

    and when validation fails for Password field it returns error as:

    {
      "-": "the length must be between 8 and 18"
    }
    

    Is there any way so that i can pass field name to it ?

    opened by Siltaz 0
  • How can I get a incorrect value or validation parameters?

    How can I get a incorrect value or validation parameters?

    Hello

    I need to return user-friendly errors for example 'test' is out of range ('foo', 'bar'). I tried to use custom errors for change message but I cannot get validation parameters and validated value. Something like that was implemented in Length validation.

    opened by tochka 0
  • Benchmarks, why is Ozzo the worst?

    Benchmarks, why is Ozzo the worst?

    Can I ask you why is Ozzo the worst in these benchmarks?

    https://github.com/frederikhors/bench-golang-validators

    goos: windows
    goarch: amd64
    pkg: benchGolangValidators
    BenchmarkAsaskevich-8              94460             12505 ns/op            5439 B/op         72 allocs/op
    BenchmarkPlayground-8             704642              1784 ns/op             227 B/op         14 allocs/op
    BenchmarkSaddam-8                  97534             12125 ns/op            1858 B/op         68 allocs/op
    BenchmarkBuffalo-8                235267              5033 ns/op             756 B/op         15 allocs/op
    BenchmarkOzzo-8                    14416             81325 ns/op            5653 B/op        112 allocs/op
    PASS
    ok      benchGolangValidators   7.300s
    
    opened by frederikhors 6
Releases(v4.3.0)
  • v4.3.0(Oct 19, 2020)

    • #128 Added the capability of validating a dynamic map whose structure is specified via validation rules
    • #130 Added the support for context-aware validation for When and Each rules

    Thanks to @NathanBaulch for the great contribution!

    Source code(tar.gz)
    Source code(zip)
  • v4.2.2(Aug 13, 2020)

    • #114 Fallback to field name for ignored JSON fields (thanks to @seriousben!)
    • #123 Added Skip.When() to support conditional skipping validation rules (thanks to @maratori!)
    Source code(tar.gz)
    Source code(zip)
  • v4.2.0(Apr 26, 2020)

    • Upgraded govalidator to v10 and added the EmailFormat validation rule to validate an email address without checking the existence of MX record
    • Fixes #103: Modified the reDomain regex to support upper/lowercase for the is.Domain rule. (thanks to @TheSecEng)
    • Fixes #105: fixed typo of ErrCurrencyCode (thanks to @maratori)
    • Fixes #106: Added the Nil and Empty rules (thanks to @samber)
    • Fixes #107: Added the Else clause to the When conditional validation construct (thanks to @samber)
    • Fixes #109: Fixed the bug in the Length rule when requiring zero length (thanks to @0perl)
    • Fixes #110: RequiredRule struct is made public (thanks to @erdaltsksn)
    Source code(tar.gz)
    Source code(zip)
  • v4.1.0(Feb 7, 2020)

    • #95, #96: Added validation.When() and validation.Required.When() to support conditional validation. Thanks to @mehran-prs for the great contribution to this new feature!
    Source code(tar.gz)
    Source code(zip)
  • v4.0.0(Jan 30, 2020)

    • #91: Updated all built-in validation rules to use validation.Error as the validation error type. It supports error code and parameter placeholders in error messages, which allows checking validation errors programmatically and translating error messages based on error codes. Thanks to @mehran-prs for the great contribution to this new feature!
    • Improved the performance of Errors.Error(). Thanks to @geekflyer for the profiling work and code contribution!
    Source code(tar.gz)
    Source code(zip)
  • v3.8.1(Dec 5, 2019)

    • Fixes #72: The validation.In rule now supports less restrictive equality comparison by using reflect.DeepEqual()
    • Fixes #63: Built-in validation rules are created as structs instead of pointers to improve memory usage performance
    Source code(tar.gz)
    Source code(zip)
  • v3.8.0(Dec 4, 2019)

  • v3.7.0(Dec 3, 2019)

  • v3.6.0(Aug 7, 2019)

  • v3.5.0(Sep 29, 2018)

    • Added MultipleOf rule (see #49)
    • Better error message for Length rule when Min and Max are the same
    • Bumped up Go version requirement to 1.8 and above
    Source code(tar.gz)
    Source code(zip)
  • v3.4.0(Jun 22, 2018)

    • Added Not-in rule (#40)
    • Added Sub-domain rule (#42)
    • Added domain rule (#43)
    • Added empty check support for time.Time (#48)
    • Added E164 phone rule (#46)
    Source code(tar.gz)
    Source code(zip)
  • v3.3(Sep 13, 2017)

  • v3.2(Jul 3, 2017)

  • v3.1(Mar 23, 2017)

  • v3.0.2(Mar 16, 2017)

  • v3.0.1(Feb 13, 2017)

  • v3.0(Feb 12, 2017)

    This release revamps the way of declaring struct validation rules, based on the discussion in #12. It introduces BC-breaking changes. Please refer to the upgrade instructions for more details.

    The following changes are introduced in this major release:

    • A new way of declaring struct field validation rules. See #12 for more information.
    • Added Errors.Filter() to support composing arbitrary validation errors.
    • Replaced Range rule with two new rules: Min and Max.
    • Added validation.By() to support wrapping a function into a validation rule.
    Source code(tar.gz)
    Source code(zip)
  • v2.2(Feb 8, 2017)

  • v2.1(Feb 7, 2017)

  • v2.0(Aug 1, 2016)

    • CHG: Removed the context parameter from the Rule.Validate() interface method. This is BC-breaking change. If your particular rule relies on the struct context, you may define it as a closure or pass the context during the rule creation.
    Source code(tar.gz)
    Source code(zip)
  • v1.0(Jul 30, 2016)

Owner
Ozzo Framework
Ozzo is a Go (golang) framework consisting of fully decoupled packages supporting rapid Web application development.
Ozzo Framework
[Go] Package of validators and sanitizers for strings, numerics, slices and structs

govalidator A package of validators and sanitizers for strings, structs and collections. Based on validator.js. Installation Make sure that Go is inst

Alex Saskevich 4.8k Jul 25, 2021
Gookit 448 Jul 21, 2021
This package provides a framework for writing validations for Go applications.

github.com/gobuffalo/validate This package provides a framework for writing validations for Go applications. It does provide you with few validators,

Buffalo - The Go Web Eco-System 59 Apr 1, 2021
Validate Golang request data with simple rules. Highly inspired by Laravel's request validation.

Validate golang request data with simple rules. Highly inspired by Laravel's request validation. Installation Install the package using $ go get githu

Saddam H 999 Jul 11, 2021
jio is a json schema validator similar to joi

jio Make validation simple and efficient ! 中文文档 Why use jio? Parameter validation in Golang is really a cursing problem. Defining tags on structs is n

faceair 60 Jul 23, 2021
:100:Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving

Package validator Package validator implements value validations for structs and individual fields based on tags. It has the following unique features

Go Playgound 8.3k Jul 23, 2021
:balloon: A lightweight struct validator for Go

gody Go versions supported Installation go get github.com/guiferpa/gody/v2 Usage package main import ( "encoding/json" "fmt" "net/http"

Guilherme Paixão 49 Feb 18, 2021
An interesting go struct tag expression syntax for field validation, etc.

go-tagexpr An interesting go struct tag expression syntax for field validation, etc. Usage Validator: A powerful validator that supports struct tag ex

Bytedance Inc. 923 Jul 21, 2021
Library providing opanapi3 and Go types for store/validation and transfer of ISO-4217, ISO-3166, and other types.

go-types This library has been created with the purpose to facilitate the store, validation, and transfer of Go ISO-3166/ISO-4217/timezones/emails/URL

Mikalai Konan 11 Jul 15, 2021
Swagger builder and input validation for Go servers

crud A Swagger/OpenAPI builder and validation library for building HTTP/REST APIs. Heavily inspired by hapi and the hapi-swagger projects. No addition

Jake Coffman 14 May 25, 2021
Struct validation using tags

Govalid Use Govalid to validate structs. Documentation For full documentation see pkg.go.dev. Example package main import ( "fmt" "log" "strings"

Travis Harmon 22 Jun 22, 2021
Simple JSON type checking.

go-map-schema Table of Contents Overview Use Case Do I Really Need This? Examples Usage Full Code Output Universal Type Names Overview go-map-schema i

Jessie 81 Jun 30, 2021
Go package containing implementations of efficient encoding, decoding, and validation APIs.

encoding Go package containing implementations of encoders and decoders for various data formats. Motivation At Segment, we do a lot of marshaling and

Segment 740 Jul 24, 2021
A norms and conventions validator for Terraform

This tool will help you ensure that a terraform folder answer to your norms and conventions rules. This can be really useful in several cases : You're

Thibault Hazelart 58 Jul 3, 2021