💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
joeybloggs 55f9e44ce5 Update benchmarks for new go 1.5! 9 years ago
examples Add RegisterCustomTypeFunc for easier adding of CustomTypeFunc 10 years ago
.gitignore add file and README example 10 years ago
LICENSE Initial commit 10 years ago
README.md Update benchmarks for new go 1.5! 9 years ago
baked_in.go Update verbiage for unique features for v7 cross struct cross field validation tags. 9 years ago
benchmarks_test.go Backport v7 updates 9 years ago
doc.go Add documentation for cross struct validation tags + Struct Partials 9 years ago
examples_test.go Update examples_test.go 9 years ago
regexes.go Updated documentation for new v6 10 years ago
util.go Backport v7 updates 9 years ago
validator.go Backport v7 updates 9 years ago
validator_test.go Backport v7 updates 9 years ago

README.md

Package validator

Join the chat at https://gitter.im/bluesuncorp/validator Build Status Coverage Status GoDoc

Package validator implements value validations for structs and individual fields based on tags.

It has the following unique features:

  • Cross Field and Cross Struct validations by using validation tags or custom validators.
  • Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
  • Handles type interface by determining it's underlying type prior to validation.
  • Handles custom field types such as sql driver Valuer see Valuer

Installation

Use go get.

go get gopkg.in/bluesuncorp/validator.v7

or to update

go get -u gopkg.in/bluesuncorp/validator.v7

Then import the validator package into your own code.

import "gopkg.in/bluesuncorp/validator.v7"

Usage and documentation

Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v7 for detailed usage docs.

Examples:

Struct & Field validation

package main

import (
	"fmt"

	"gopkg.in/bluesuncorp/validator.v6"
)

// User contains user information
type User struct {
	FirstName      string     `validate:"required"`
	LastName       string     `validate:"required"`
	Age            uint8      `validate:"gte=0,lte=130"`
	Email          string     `validate:"required,email"`
	FavouriteColor string     `validate:"hexcolor|rgb|rgba"`
	Addresses      []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}

// Address houses a users address information
type Address struct {
	Street string `validate:"required"`
	City   string `validate:"required"`
	Planet string `validate:"required"`
	Phone  string `validate:"required"`
}

var validate *validator.Validate

func main() {

	config := validator.Config{
		TagName:         "validate",
		ValidationFuncs: validator.BakedInValidators,
	}

	validate = validator.New(config)

	validateStruct()
	validateField()
}

func validateStruct() {

	address := &Address{
		Street: "Eavesdown Docks",
		Planet: "Persphone",
		Phone:  "none",
	}

	user := &User{
		FirstName:      "Badger",
		LastName:       "Smith",
		Age:            135,
		Email:          "Badger.Smith@gmail.com",
		FavouriteColor: "#000",
		Addresses:      []*Address{address},
	}

	// returns nil or ValidationErrors ( map[string]*FieldError )
	errs := validate.Struct(user)

	if errs != nil {

		fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag
		//	                         Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag
		err := errs["User.Addresses[0].City"]
		fmt.Println(err.Field) // output: City
		fmt.Println(err.Tag)   // output: required
		fmt.Println(err.Kind)  // output: string
		fmt.Println(err.Type)  // output: string
		fmt.Println(err.Param) // output:
		fmt.Println(err.Value) // output:

		// from here you can create your own error messages in whatever language you wish
		return
	}

	// save user to database
}

func validateField() {
	myEmail := "joeybloggs.gmail.com"

	errs := validate.Field(myEmail, "required,email")

	if errs != nil {
		fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
		return
	}

	// email ok, move on
}

Custom Field Type

package main

import (
	"database/sql"
	"database/sql/driver"
	"fmt"
	"reflect"

	"gopkg.in/bluesuncorp/validator.v7"
)

// DbBackedUser User struct
type DbBackedUser struct {
	Name sql.NullString `validate:"required"`
	Age  sql.NullInt64  `validate:"required"`
}

func main() {

	config := validator.Config{
		TagName:         "validate",
		ValidationFuncs: validator.BakedInValidators,
	}

	validate := validator.New(config)

	// register all sql.Null* types to use the ValidateValuer CustomTypeFunc
	validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})

	x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
	errs := validate.Struct(x)

	if len(errs) > 0 {
		fmt.Printf("Errs:\n%+v\n", errs)
	}
}

// ValidateValuer implements validator.CustomTypeFunc
func ValidateValuer(field reflect.Value) interface{} {
	if valuer, ok := field.Interface().(driver.Valuer); ok {
		val, err := valuer.Value()
		if err == nil {
			return val
		}
		// handle the error how you want
	}
	return nil
}

Benchmarks

Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go 1.5
PASS
BenchmarkFieldSuccess-4                            	 5000000	       290 ns/op	      16 B/op	       1 allocs/op
BenchmarkFieldFailure-4                            	 5000000	       286 ns/op	      16 B/op	       1 allocs/op
BenchmarkFieldDiveSuccess-4                        	  500000	      2497 ns/op	     384 B/op	      19 allocs/op
BenchmarkFieldDiveFailure-4                        	  500000	      3022 ns/op	     752 B/op	      23 allocs/op
BenchmarkFieldCustomTypeSuccess-4                  	 3000000	       446 ns/op	      32 B/op	       2 allocs/op
BenchmarkFieldCustomTypeFailure-4                  	 2000000	       778 ns/op	     416 B/op	       6 allocs/op
BenchmarkFieldOrTagSuccess-4                       	 1000000	      1287 ns/op	      32 B/op	       2 allocs/op
BenchmarkFieldOrTagFailure-4                       	 1000000	      1125 ns/op	     400 B/op	       6 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4           	 1000000	      1225 ns/op	      80 B/op	       5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4           	 1000000	      1742 ns/op	     608 B/op	      13 allocs/op
BenchmarkStructPartialSuccess-4                    	 1000000	      1304 ns/op	     400 B/op	      11 allocs/op
BenchmarkStructPartialFailure-4                    	 1000000	      1818 ns/op	     784 B/op	      16 allocs/op
BenchmarkStructExceptSuccess-4                     	 2000000	       869 ns/op	     368 B/op	       9 allocs/op
BenchmarkStructExceptFailure-4                     	 1000000	      1308 ns/op	     400 B/op	      11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4           	 2000000	       973 ns/op	     128 B/op	       6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4           	 1000000	      1519 ns/op	     528 B/op	      11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4	 1000000	      1382 ns/op	     160 B/op	       8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4	 1000000	      1931 ns/op	     560 B/op	      13 allocs/op
BenchmarkStructSimpleSuccess-4                     	 1000000	      1132 ns/op	      48 B/op	       3 allocs/op
BenchmarkStructSimpleFailure-4                     	 1000000	      1735 ns/op	     560 B/op	      11 allocs/op
BenchmarkStructSimpleSuccessParallel-4             	 3000000	       363 ns/op	      48 B/op	       3 allocs/op
BenchmarkStructSimpleFailureParallel-4             	 2000000	       705 ns/op	     560 B/op	      11 allocs/op
BenchmarkStructComplexSuccess-4                    	  200000	      6935 ns/op	     432 B/op	      27 allocs/op
BenchmarkStructComplexFailure-4                    	  200000	     11059 ns/op	    2920 B/op	      69 allocs/op
BenchmarkStructComplexSuccessParallel-4            	 1000000	      2220 ns/op	     432 B/op	      27 allocs/op
BenchmarkStructComplexFailureParallel-4            	  300000	      4739 ns/op	    2920 B/op	      69 allocs/op

How to Contribute

There will always be a development branch for each version i.e. v1-development. In order to contribute, please make your pull requests against those branches.

If the changes being proposed or requested are breaking changes, please create an issue, for discussion or create a pull request against the highest development branch for example this package has a v1 and v1-development branch however, there will also be a v2-development brach even though v2 doesn't exist yet.

I strongly encourage everyone whom creates a custom validation function to contribute them and help make this package even better.

License

Distributed under MIT License, please see license file in code for more details.