Documentation
¶
Overview ¶
Package validate contains field validations for Go structs, appropriated to use with [*lit.Request].
There are two ways a struct can be validated:
- Explicitly, by calling Fields and passing the validations required;
- Implicitly, by making the struct implement Validatable with a pointer receiver and using the binding functions.
When a validation fails, Fields use the Message and Fields attributes of the Field struct to build the validation error.
Custom validations ¶
A validation is simply an instance of the struct Field. In order to create a new validation, is enough to just create your own instance, passing the arguments. You could also change only the Message field, if that meets your use case. Check the package-level examples.
Example (CustomMessage) ¶
package main import ( "fmt" "net/http" "net/http/httptest" "github.com/jvcoutinho/lit" "github.com/jvcoutinho/lit/bind" "github.com/jvcoutinho/lit/render" "github.com/jvcoutinho/lit/validate" ) type DivideRequest struct { A int `query:"a"` B int `query:"b"` } func (r *DivideRequest) Validate() []validate.Field { notZeroValidation := validate.NotEqual(&r.B, 0) notZeroValidation.Message = "{0} should not be zero: invalid division" return []validate.Field{ notZeroValidation, } } // Divide returns the division of two integers, given the second is not zero. func Divide(r *lit.Request) lit.Response { req, err := bind.Query[DivideRequest](r) if err != nil { return render.BadRequest(err) } return render.OK(req.A / req.B) } func main() { r := lit.NewRouter() r.GET("/div", Divide) res := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/div?a=4&b=2", nil) r.ServeHTTP(res, req) fmt.Println(res.Body) res = httptest.NewRecorder() req = httptest.NewRequest(http.MethodGet, "/div?a=2&b=0", nil) r.ServeHTTP(res, req) fmt.Println(res.Body) }
Output: 2 {"message":"b should not be zero: invalid division"}
Example (CustomValidations) ¶
package main import ( "fmt" "net/http" "net/http/httptest" "github.com/jvcoutinho/lit" "github.com/jvcoutinho/lit/bind" "github.com/jvcoutinho/lit/render" "github.com/jvcoutinho/lit/validate" ) // Even validates if target is an even number. func Even(target *int) validate.Field { return validate.Field{ Valid: target != nil && *target%2 == 0, Message: "{0} should be an odd number", Fields: []any{target}, } } // MultipleField validates if target is multiple of field. func MultipleField(target *int, field *int) validate.Field { return validate.Field{ Valid: target != nil && field != nil && *target%*field == 0, Message: "{1} should be divisible by {0}", Fields: []any{target, field}, } } type EvenNumbersBetweenRequest struct { A int `query:"a"` B int `query:"b"` } func (r *EvenNumbersBetweenRequest) Validate() []validate.Field { return []validate.Field{ Even(&r.A), MultipleField(&r.B, &r.A), } } // EvenNumbersBetween compute the even numbers between two numbers, given the first is even and // the second is multiple of the first. func EvenNumbersBetween(r *lit.Request) lit.Response { req, err := bind.Query[EvenNumbersBetweenRequest](r) if err != nil { return render.BadRequest(err) } evenNumbersBetween := make([]int, 0) for i := req.A; i <= req.B; i += 2 { evenNumbersBetween = append(evenNumbersBetween, i) } return render.OK(evenNumbersBetween) } func main() { r := lit.NewRouter() r.GET("/", EvenNumbersBetween) res := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/?a=4&b=20", nil) r.ServeHTTP(res, req) fmt.Println(res.Body) res = httptest.NewRecorder() req = httptest.NewRequest(http.MethodGet, "/?a=2&b=3", nil) r.ServeHTTP(res, req) fmt.Println(res.Body) }
Output: [4,6,8,10,12,14,16,18,20] {"message":"a should be divisible by b"}
Index ¶
- func Fields[T any](target *T, validations ...Field) error
- type Error
- type Field
- func After(target *time.Time, value time.Time) Field
- func Before(target *time.Time, value time.Time) Field
- func Between[T constraints.Ordered](target *T, min, max T) Field
- func BetweenLength[T any](target *T, min, max int) Field
- func BetweenTime(target *time.Time, min, max time.Time) Field
- func DateTime(target *string, layout string) Field
- func Email(target *string) Field
- func Empty[T any](target *T) Field
- func Equal[T comparable](target *T, value T) Field
- func EqualField[T comparable](target *T, field *T) Field
- func Greater[T constraints.Ordered](target *T, value T) Field
- func GreaterField[T constraints.Ordered](target *T, field *T) Field
- func GreaterOrEqual[T constraints.Ordered](target *T, value T) Field
- func GreaterOrEqualField[T constraints.Ordered](target *T, field *T) Field
- func HasPrefix(target *string, prefix string) Field
- func HasSuffix(target *string, suffix string) Field
- func IPAddress(target *string) Field
- func IPv4Address(target *string) Field
- func IPv6Address(target *string) Field
- func Length[T any](target *T, value int) Field
- func Less[T constraints.Ordered](target *T, value T) Field
- func LessField[T constraints.Ordered](target *T, field *T) Field
- func LessOrEqual[T constraints.Ordered](target *T, value T) Field
- func LessOrEqualField[T constraints.Ordered](target *T, field *T) Field
- func Lowercase(target *string) Field
- func MaxLength[T any](target *T, value int) Field
- func MinLength[T any](target *T, value int) Field
- func NotEmpty[T any](target *T) Field
- func NotEqual[T comparable](target *T, value T) Field
- func NotEqualField[T comparable](target *T, field *T) Field
- func OneOf[T comparable](target *T, values ...T) Field
- func Required[T any](target *T) Field
- func Substring(target *string, substring string) Field
- func UUID(target *string) Field
- func Uppercase(target *string) Field
- type Validatable
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Fields ¶
Fields validates the fields of a struct of type T.
It uses the tags of the binding functions (such as "uri", "query" or "json") from the fields to build a message for the user in case the validation fails. If none of these tags are set or are set as the empty string, Fields uses the field's name instead.
If T is not a struct type, Fields panics.
Example ¶
package main import ( "fmt" "net/http" "net/http/httptest" "strings" "github.com/jvcoutinho/lit" "github.com/jvcoutinho/lit/bind" "github.com/jvcoutinho/lit/validate" ) func main() { req := httptest.NewRequest(http.MethodPost, "/books", strings.NewReader(` {"name": "Percy Jackson", "publishYear": 2007} `)) r := lit.NewRequest(req) type RequestBody struct { Name string `json:"name"` PublishYear int `json:"publishYear"` } body, _ := bind.Body[RequestBody](r) err := validate.Fields(&body, validate.Greater(&body.PublishYear, 2009), validate.Less(&body.PublishYear, 2020), validate.HasPrefix(&body.Name, "A"), ) if err != nil { fmt.Println(err) } }
Output: publishYear should be greater than 2009; name should start with "A"
Types ¶
type Error ¶
type Error struct { // Validations that have failed. Violations []Field }
Error occurs when at least one Field validation fails in validation steps.
type Field ¶
type Field struct { // Determines if this validation has succeeded. Valid bool // A user-friendly message that can be displayed if the validation fails. In this case, // "{i}" placeholders, where i is the index of the replacement in Fields, are replaced by the field's tag values. Message string // Pointers to the fields involved in this validation. It is a slice because the validation // can have multiple fields as arguments. Fields []any }
Field represents a field validation.
func Between ¶
func Between[T constraints.Ordered](target *T, min, max T) Field
Between validates if target is greater than min and less than max.
func BetweenLength ¶
BetweenLength validates if target has a length greater or equal than min and less or equal than max.
If target is not a pointer to a slice, array, string or map, BetweenLength panics.
func BetweenTime ¶
BetweenTime validates if target is after min and before max.
func DateTime ¶
DateTime validates if target is a valid date-time string.
Note that binding functions bind strings to fields with type time.Time if the layout is time.RFC3339, validating them in the process.
func Empty ¶
Empty validates if target is empty.
If target is not a pointer to a slice, array, string or map, Empty panics.
func Equal ¶
func Equal[T comparable](target *T, value T) Field
Equal validates if target is equal to value.
func EqualField ¶
func EqualField[T comparable](target *T, field *T) Field
EqualField validates if target is equal to the value of field.
func Greater ¶
func Greater[T constraints.Ordered](target *T, value T) Field
Greater validates if target is greater than value.
func GreaterField ¶
func GreaterField[T constraints.Ordered](target *T, field *T) Field
GreaterField validates if target is greater than the value of field.
func GreaterOrEqual ¶
func GreaterOrEqual[T constraints.Ordered](target *T, value T) Field
GreaterOrEqual validates if target is greater or equal than value.
func GreaterOrEqualField ¶
func GreaterOrEqualField[T constraints.Ordered](target *T, field *T) Field
GreaterOrEqualField validates if target is greater or equal than the value of field.
func IPv4Address ¶
IPv4Address validates if target is a valid IPv4 address.
func IPv6Address ¶
IPv6Address validates if target is a valid IPv6 address.
func Length ¶
Length validates if target has a length of value.
If target is not a pointer to a slice, array, string or map, Length panics.
func Less ¶
func Less[T constraints.Ordered](target *T, value T) Field
Less validates if target is less than value.
func LessField ¶
func LessField[T constraints.Ordered](target *T, field *T) Field
LessField validates if target is less than the value of field.
func LessOrEqual ¶
func LessOrEqual[T constraints.Ordered](target *T, value T) Field
LessOrEqual validates if target is less or equal than value.
func LessOrEqualField ¶
func LessOrEqualField[T constraints.Ordered](target *T, field *T) Field
LessOrEqualField validates if target is less or equal than the value of field.
func MaxLength ¶
MaxLength validates if target has a length less or equal than value.
If target is not a pointer to a slice, array, string or map, MaxLength panics.
func MinLength ¶
MinLength validates if target has a length greater or equal than value.
If target is not a pointer to a slice, array, string or map, MinLength panics.
func NotEmpty ¶
NotEmpty validates if target is not empty.
If target is not a pointer to a slice, array, string or map, NotEmpty panics.
func NotEqual ¶
func NotEqual[T comparable](target *T, value T) Field
NotEqual validates if target is not equal to value.
func NotEqualField ¶
func NotEqualField[T comparable](target *T, field *T) Field
NotEqualField validates if target is not equal to the value of field.
func OneOf ¶
func OneOf[T comparable](target *T, values ...T) Field
OneOf validates if target is one of values.
func Required ¶
Required validates that target is not nil. Suited for when target is a pointer field.
type Validatable ¶
type Validatable interface { // Validate returns a list of field validations. Validate() []Field }
Validatable structs can be validated.