Awesome
Fast and intuitive validation library for Go
This lib uses the Is...
validation functions from the govalidator project.
Installation
go get github.com/tiendc/go-validator
Usage
General usage
import (
vld "github.com/tiendc/go-validator"
)
type Person struct {
FirstName string
LastName string
Birthdate time.Time
Unemployed bool
Salary uint
Rank string
WorkEmail string
Projects []string
TaskMap map[string]Task
}
var p Person
errs := vld.Validate(
// Validate first and last names separately
vld.StrLen(&p.FirstName, 3, 30).OnError(
vld.SetField("first_name", nil),
vld.SetCustomKey("ERR_VLD_PERSON_FIRST_NAME_INVALID"),
),
vld.StrLen(&p.FirstName, 3, 30).OnError(
vld.SetField("last_name", nil),
vld.SetCustomKey("ERR_VLD_PERSON_LAST_NAME_INVALID"),
),
// OR use this to produce only one error when one of them fails
vld.Group(
vld.StrLen(&p.FirstName, 3, 30),
vld.StrLen(&p.LastName, 3, 30),
).OnError(
vld.SetField("name", nil),
vld.SetCustomKey("ERR_VLD_PERSON_NAME_INVALID"),
),
// Birthdate is optional, but when it's present, it must be within 1950 and now
vld.When(!p.Birthdate.IsZero()).Then(
vld.TimeRange(p.Birthdate, <1950-01-01>, time.Now()).OnError(...),
)
vld.When(!p.Unemployed).Then(
vld.Required(&p.Salary),
// Work email must be valid
vld.StrIsEmail(&p.WorkEmail),
// Rank must be one of the constants
vld.StrIn(&p.Rank, "Employee", "Manager", "Director"),
vld.Case(
vld.When(p.Rank == "Manager").Then(vld.NumGT(&p.Salary, 10000)),
vld.When(p.Rank == "Director").Then(vld.NumGT(&p.Salary, 30000)),
).Default(
vld.NumLT(&p.Salary, 10000),
),
// Projects are optional, but when they are present, they must be unique and sorted
vld.When(len(p.Projects) > 0).Then(
vld.SliceUnique(p.Projects).OnError(...),
vld.SliceSorted(p.Projects).OnError(...),
)
).Else(
// When person is unemployed
vld.NumEQ(&p.Salary, 0),
vld.StrEQ(&p.WorkEmail, ""),
),
// Validate slice elements
vld.Slice(p.Projects).ForEach(func(elem int, index int, validator ItemValidator) {
validator.Validate(
vld.StrLen(&elem, 10, 30).OnError(
vld.SetField(fmt.Sprintf("projects[%d]", index), nil),
vld.SetCustomKey("ERR_VLD_PROJECT_NAME_INVALID"),
),
)
}),
// Validate map entries
vld.Map(p.TaskMap).ForEach(func(k string, v Task, validator ItemValidator) {
validator.Validate(
vld.StrLen(&v.Name, 10, 30).OnError(
vld.SetField(fmt.Sprintf("taskMap[%s].name", k), nil),
vld.SetCustomKey("ERR_VLD_TASK_NAME_INVALID"),
),
)
}),
// OTHER FUNCTIONS
// Pass if at least one of the validations passes
vld.OneOf(
// List of validations
),
// Pass if exact one of the validations passes
vld.ExactOneOf(
// List of validations
),
// Pass if none of the validations passes
vld.NotOf(
// List of validations
),
)
for _, e := range errs {
detail, warnErr := e.BuildDetail()
fmt.Printf("%+v\n", detail)
}
Error message localization
- Method 1: inline localization (not recommended)
errs := Validate(
NumLTE(&p.Age, 40).OnError(
// Override the default template in english
SetTemplate("Tuổi nhân viên phải nhỏ hơn hoặc bằng {{.Max}}"),
),
)
for _, e := range errs {
detail, warnErr := e.BuildDetail()
fmt.Printf("%+v\n", detail)
}
- Method 2: using another localization lib (recommended)
// Supposed you have 2 files defining error messages
// In `error_messages.en`:
// ERR_VLD_EMPLOYEE_AGE_TOO_BIG = "Employee {{.EmployeeName}} has age bigger than {{.Max}}"
// In `error_messages.vi`:
// ERR_VLD_EMPLOYEE_AGE_TOO_BIG = "Nhân viên {{.EmployeeName}} có tuổi lớn hơn {{.Max}}"
errs := Validate(
NumLTE(&p.Age, 40).OnError(
// Custom param (the default template doesn't have this one)
SetParam("EmployeeName", p.Name),
// Custom key to define custom template to use
SetCustomKey("ERR_VLD_EMPLOYEE_AGE_TOO_BIG"),
),
)
for _, e := range errs {
errKey := e.CustomKey()
errParams : = e.Params() // or e.ParamsWithFormatter()
errorMsg := translationFunction(errKey, errParams) // You need to provide this function
fmt.Printf("%+v\n", errorMsg)
}
Custom error param formatter
errs := Validate(
NumLT(&budget, 1000000).OnError(
SetField("Budget", nil),
),
)
// e.BuildDetail() may produce message `Budget must be less than 1000000`,
// but you may want a message like: `Budget must be less than 1,000,000`.
// Let's use a custom formatter
errs := Validate(
NumLT(&budget, 1000000).OnError(
SetField("Budget", nil),
SetNumParamFormatter(NewDecimalFormatFunc('.', ',', "%f")),
),
)
Contributing
- You are welcome to make pull requests for new functions and bug fixes.