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.

Comments
  • 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
  • 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
  • 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
  • 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
  • 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 8
  • 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 8
  • 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
  • required_with

    required_with

    How do you think about required_with_one and required_with_all rules that specify some fields as required if other fields are not empty? I think we really need these two rules.

    Something like this:

    // Copyright 2016 Qiang Xue. All rights reserved.
    // Use of this source code is governed by a MIT-style
    // license that can be found in the LICENSE file.
    
    package validation
    
    // ErrInInvalid is the error that returns in case of an invalid value for "required_with" rules.
    var ErrRequiredWithInvalid = NewError("validation_required_with_is_blank", "field can not be blank.")
    
    func RequiredWithOne(values ...interface{}) RequiredWithRule {
    	return RequiredWithRule{
    		elements: values,
    		withAll:  false,
    		err:      ErrRequiredWithInvalid,
    	}
    }
    
    func RequiredWithAll(values ...interface{}) RequiredWithRule {
    	return RequiredWithRule{
    		elements: values,
    		withAll:  true,
    		err:      ErrRequiredWithInvalid,
    	}
    }
    
    // RequiredWithRule is a validation rule that validates if specified values is not nil, so current field is required.
    type RequiredWithRule struct {
    	elements []interface{}
    	withAll  bool
    	err      Error
    }
    
    // Validate checks if the given value is valid or not.
    func (r RequiredWithRule) Validate(value interface{}) error {
    	allFilled := true
    	oneFilled := false
    
    	for _, elem := range r.elements {
    		elem, isNil := Indirect(elem)
    		filled := !(isNil || IsEmpty(elem))
    
    		allFilled = allFilled && filled
    		oneFilled = oneFilled || filled
    	}
    
    	if (r.withAll && allFilled) || (!r.withAll && oneFilled) {
    		return Validate(value, Required.ErrorObject(r.err))
    	}
    
    	return r.err
    }
    
    // Error sets the error message for the rule.
    func (r RequiredWithRule) Error(message string) RequiredWithRule {
    	r.err = r.err.SetMessage(message)
    	return r
    }
    
    // ErrorObject sets the error struct for the rule.
    func (r RequiredWithRule) ErrorObject(err Error) RequiredWithRule {
    	r.err = err
    	return r
    }
    
    
    opened by mehran-prs 6
  • Custom validation rule with additional parameters

    Custom validation rule with additional parameters

    Hello

    The validation.By method offers no way to use additional parameters. I would like to modify it to accept a varidic list of parameters after the value to validate, like so:

    func checkAbc(value interface{}, args ...interface{}) error {
    	s, _ := value.(string)
            caseSensitive, _ := args[0].(bool)
    	if !caseSensitive && s != "abc" {
    		return errors.New("must be abc")
    	}
    	if caseSensitive && !EqualFold(s, "abc") {
    		return errors.New("must be abc")
    	}
    	return nil
    }
    
    err := validation.Validate("xyz", validation.By(checkAbc, true))
    

    What do you think about it ?

    opened by wI2L 6
  • How can I validation field array ?

    How can I validation field array ?

    I have struct like this

    type ReviewJson struct {
    	Business string   `json:"business"  form:"business"`
    	Feedback string   `json:"feedback" form:"feedback"`
    	Point    int      `json:"point" form:"point"`
    	Photos   []string `form:"photos[]"`
    }
    

    I wanna validation field Photos, for each photo has valid with MongoId.

    opened by minhthuan274 6
  • validation.In

    validation.In

    Hi,

    I'm trying to validate a struct that contains this validation

    validation.Field(&bc.Priority, validation.Required, validation.In(1, 2, 3, 4).Error("must be 1, 2, 3 or 4")),
    

    If I pass 1,2, 3 or 4 is always returns that error "must be 1, 2, 3 or 4"

    What I'm I doing wrong?

    opened by Narven 5
  • [discussion] How to validate an optional field?

    [discussion] How to validate an optional field?

    Consider this struct:

    type Foo struct {
        Bar *int `json:"bar"`
    }
    
    func (r Foo) Validate() error {
        return validation.ValidateStruct(
             &r,
             validation.Field(
                  &r.Bar,
                  validation.When(
                       r.Bar != nil,
                       validation.Required,
                       validation.Min(2),
                  ),  
             ),
        )
    }
    

    How to say if Bar provided then validate it is bigger than e.g. 2 else do not validate as it is optional? Is my solution the best effort? I mean if i'm not using validation.When(r.Bar != nil, validation.Required, ...) then it accept 0 value for Bar but that is not my intention!

    opened by aria3ppp 0
  • Update examples to reflect real US cities

    Update examples to reflect real US cities

    Proposal: Update example to validate City length between 1-50.

    Reasoning: For lazy developers like me, I started using this package by copying the examples for the addresses because I figured that they were grounded in real-world, common validation rules. I did tweak the State and Zipcode validation to be more general, but then we hit a snag when we realized that Novi, MI (4 characters) is a real US city. This led me to look into the real boundaries and I've landed on this PR as an enhancement to save headaches for other future lazy developers. :)

    Source: https://www.serviceobjects.com/blog/character-limits-in-address-lines-for-usps-ups-and-fedex/ (no minimum, so I assume 1, maximum 50)

    opened by tuan-nxcr 0
  • bug about type alias

    bug about type alias

    if I set a type alias of basic type,like []string ,then, I neet to add two function,such as ( value and scan ,mysql json need this), the bug is appened. the entire code under:

    package main
    
    import (
    	"database/sql/driver"
    	"encoding/json"
    	"fmt"
    
    	validation "github.com/go-ozzo/ozzo-validation"
    )
    
    type Tags []string
    
    // mysql 8.0 json need value and scan function, so I need type alias
    type Courses struct {
    	Tag Tags `gorm:"type:json;comment:体系名是json格式" json:"tag"`
    }
    
    // !!!!!! IF YOU REMOVE THIS FUNCTION, IT IS VALIDETED !!!!!
    // !!!!!! IF YOU ADD PONITER IN P, IT IS VALIDETED  !!!!
    func (p Tags) Value() (driver.Value, error) {
    	b, err := json.Marshal(p)
    	return b, err
    }
    
    
    func (p *Tags) Scan(input any) error {
    	return json.Unmarshal(input.([]byte), p)
    }
    
    func main() {
    
    	var courseDto Courses
    	courseDto.Tag = Tags{"student", "engineer"}
    	err := validation.ValidateStruct(&courseDto,
    		validation.Field(&courseDto.Tag, validation.Required, validation.Length(1, 3)),
    	)
    	fmt.Println(err)
    }
    

    Something strange is coming in function value !!! If p without pointer can't pass validate!

    func (p Tags) Value() (driver.Value, error) {
    	//b, err := json.Marshal(p)
    	return nil, nil
    }
    

    But, if add pointer, it pass!

    opened by ghost 2
  • is.Float with decimal places

    is.Float with decimal places

    Can I use is.Float with specification of decimal places? I will always want my float to have two decimal places

    type Foo struct {
    	Bar float32 `json:"bar"`
    }
    
    func (f Foo) Validate() error {
    	return validation.ValidateStruct(&f,
    		validation.Field(&f.Bar, is.Float)) //<=== specify decimal places
    }
    
    opened by rngallen 0
  • Check for MIME Types

    Check for MIME Types

    Is it possible to add simple checks for Content-Type/MIME type? https://en.wikipedia.org/wiki/Media_type

    type "/" [tree "."] subtype ["+" suffix]* [";" parameter]
    
    opened by acabarbaye 0
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 31, 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 14, 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 8, 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
Golang parameter validation, which can replace go-playground/validator, includes ncluding Cross Field, Map, Slice and Array diving, provides readable,flexible, configurable validation.

Checker 中文版本 Checker is a parameter validation package, can be use in struct/non-struct validation, including cross field validation in struct, elemen

Liang Yaopei 79 Dec 16, 2022
: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 12.2k Jan 1, 2023
[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 5.6k Jan 6, 2023
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 1.2k Dec 29, 2022
Data validators for Golang

golidators Golidators is a golang package, it includes basic data validation functions and regexes. Install go get github.com/eredotpkfr/golidators Ov

Erdoğan Yoksul 10 Oct 19, 2022
Gookit 824 Dec 28, 2022
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. 1.3k Jan 9, 2023
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 883 Dec 25, 2022
Error-safe value caster for golang

Caster Error-safe value caster. NewCaster Create new caster instance from value. // Signature NewCaster(data interface{}) Caster // Example import "g

Bardo Go Framework 0 Dec 6, 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 15 Nov 9, 2022
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 38 Jan 5, 2023
Opinionated go to validation library

?? valeed Your opinionated go-to validation library. Struct tag-based. Validate here, validate there, validate everywhere. Sleek and simple validation

Avré Barra 0 Jul 21, 2022
Simple module for validation inn control number

simple module for validation inn control number

Pavel 1 Sep 4, 2022
Gin Middleware to extract json tag value from playground validator's errors validation

Json Tag Extractor for Go-Playground Validator This is Gin Middleware that aim to extract json tag and than store it to FieldError.Field() object. Ins

Muhamad Surya Iksanudin 0 Jan 14, 2022
Validator - Replace the validation framework used by gin

validator Replace the validation framework used by gin replace mod:replace githu

null 2 Jan 18, 2022
: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 62 Nov 19, 2022
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 79 Dec 15, 2022
golang request validator

validator Golang 参数验证器,目前只支持POST请求,JSON格式参数验证 亮点 1、验证时只要有一个错误,错误信息立即返回 2、可自定义参数别名显示错误信息;详情见_example文件 使用 go mod -u github.com/one-gold-coin/validator

One Gold Coin 2 Sep 30, 2021
golang rule-based string validator

gosv golang rule-based string validator usage import "github.com/s0rg/gosv" var MyRules = []gosv.Rule{ gosv.MinLen(8), gosv.MaxLen(64), gosv.MinLo

Alexei Shevchenko 3 Nov 20, 2021