go-validator
go-validator copied to clipboard
Intuitive validation library for Golang
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.
License
- MIT License