A go package that has Laravel like validation.
It takes inspiration from Laravel's validation system but is not a 1 on 1 copy.
It's designed to make it easy to convert the validation rules from a Laravel project to a Go project.
go get -u github.com/mjarkk/laravalidate
package main
import (
"context"
"fmt"
"github.com/mjarkk/laravalidate"
)
func main() {
input := struct {
Name string `json:"name" validate:"required"`
}{
Name: "",
}
err := laravalidate.JsonValidate(context.Background(), nil, input)
fmt.Println(err.Error()) // The name field is required.
}
All the rules are defined in: RULES.md
Most laraval rules are supported*.
(*database related rules can be added using the dbrules
package and currently only support sql databases)
Rules can be set using the validate
tag on a struct field like:
type UserRequest struct {
Email string `json:"email" validate:"email"`
Name string `json:"name" validate:"required"`
}
Multiple rules can be set by separating them with a |
like:
type UserRequest struct {
Email string `json:"email" validate:"required|email"`
}
When validating an array the array can be validated using the validate
tag and it's elements can be validated using the validateInner
tag like:
type UserRequest struct {
// The emails list is required and must at least contain one element.
// The elements in the list must be valid email addresses.
Emails []string `json:"emails" validate:"required" validateInner:"email"`
}
Database rules are not out of the box provided as they require a database connection.
For sql connections they can be implemented using the dbrules
package.
func main() {
db, err := sql.Open(driverName, dns)
// Check err
dbrules.AddRules(db, dbrules.DefaultStyle)
// Now you can add translations!
// translations.RegisterNlTranslations()
}
type UserRequest struct {
Email string `json:"email" validate:"email"`
Name string `json:"name" validate:"required"`
}
func main() {
err := laravalidate.JsonValidate(context.Background(), nil, UserRequest{})
if err == nil {
os.Exit(0)
}
errInfo := err.(*laravalidate.ValidationError)
for _, fieldError := range errInfo.Errors {
fmt.Println("field:", fieldError.JsonPath)
for _, err := range fieldError.Errors {
fmt.Printf(" %+v\n", err)
}
}
}
For when you are converting a laravel application to a go application and want to keep the error messages the same.
type UserRequest struct {
Email string `json:"email" validate:"email"`
Name string `json:"name" validate:"required"`
}
func main() {
err := laravalidate.JsonValidate(context.Background(), nil, UserRequest{})
if err == nil {
os.Exit(0)
}
laravelErr := err.(*laravalidate.ValidationError).ToLaravelError()
laravelErrJson, err := json.MarshalIndent(laravelErr, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(laravelErrJson))
/*{
"errors": {
"email": [
"The email field must be a valid email address."
],
"name": [
"The name field is required."
]
},
"message": "Form contains errors"
}*/
}
It might be that you are not validating json messages but rather go structs or something else. In these cases you might want the go names rather than the json names in the error messages.
This can be done using the laravalidate.GoValidate
method
func main() {
// If you are using the db rules,
// you should add them before registering any translations!
translations.RegisterNlTranslations()
input := struct {
Name string `json:"name" validate:"required"`
}{
Name: "",
}
err := laravalidate.GoValidate(context.Background(), nil, input)
fmt.Println(err.Error()) // The Name field is required.
}
Sometimes you want to provide a custom error message for a specific field.
type UserRequest struct {
Email string `json:"email" validate:"email"`
Name string `json:"name" validate:"required"`
}
func (UserRequest) ValidationMessages() []laravalidate.CustomError {
return []laravalidate.CustomError{
{Key: "Email", Resolver: laravalidate.BasicMessageResolver("Bro email is required!")},
}
}
func main() {
err := laravalidate.JsonValidate(context.Background(), nil, UserRequest{})
fmt.Println(err.Error()) // Bro email is required!
}
Nested and spesific validators fields can be accessed using the .
separator.
For example when a field is nested under Foo[3].Bar.Baz[2]
you can define the keys for it the following ways:
Foo.3.Bar.Baz.2
(very strict)Foo.3.Bar.Baz.2.required
(only triggers on required rule)Foo.*.Bar.Baz.*
(lists have wild cards)Foo.Bar.Baz
(same as the prevouse one)Foo.Bar.Baz.required
(only triggers on the required rule)
There are also some variables that can be used in the custom error messages:
:attribute
- The name of the field:value
- The value of the field:other
- If the value is compared to another value this will be the other value:date
- The date that is being validated in the DateTime format2006-01-02 15:04:05
:args
- All the argument provided to the validator:arg0..x
(arg4
) - A specific argument provided to the validator by index (0 based)
You can provide a list of languages to the JsonValidate
function to get translated errors.
This only works if you have registered custom error messages for a specific language.
package main
import (
"context"
"fmt"
"github.com/mjarkk/laravalidate"
"github.com/mjarkk/laravalidate/translations"
"golang.org/x/text/language"
)
func main() {
// Note that we have to register the translations before we can use them.
translations.RegisterNlTranslations()
input := struct {
Name string `json:"name" validate:"required"`
}{
Name: "",
}
err := laravalidate.JsonValidate(
context.Background(),
[]language.Tag{
language.Dutch,
},
input,
)
fmt.Println(err.Error()) // Het name veld is verplicht.
}
Out of the box supported languages are:
- English (default)
- German
translations.RegisterDeTranslations()
- Dutch
translations.RegisterNlTranslations()
- French
translations.RegisterFrTranslations()
- Spanish
translations.RegisterEsTranslations()
See how other translations are done inside of the ./translations folder
// Accepted is a custom validator that checks if the value is accepted.
//
// It returns 2 values:
// - string : Hint for the error message, this can be used to change error messages based on the hint.
// - bool : Passes, if false the validation failed and you'll get an error message
//
// Note that this validator is already part of the laravel validation rules and just here for example.
func Accepted(ctx *ValidatorCtx) (string, bool) {
// Unwrap the pointer to the actual value if it is a pointer.
// Note that this might result in a type but no value inside the ctx.
ctx.UnwrapPointer()
// Check if the value type is one of the following:
if !ctx.IsKind(
reflect.Bool,
reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
) {
return "invalid_type", false
}
// Check if the ctx has a value
if !ctx.HasValue() {
return "unacceptable", false
}
switch ctx.Kind() {
case reflect.Bool:
if !ctx.Value.Bool() {
return "unacceptable", false
}
case reflect.String:
switch ctx.Value.String() {
case "yes", "on", "1", "true":
return "", true
}
return "unacceptable", false
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if ctx.Value.Int() == 1 {
return "", true
}
return "unacceptable", false
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if ctx.Value.Uint() == 1 {
return "", true
}
return "unacceptable", false
default:
return "invalid_type", false
}
return "", true
}
func main() {
laravalidate.RegisterValidator("accepted", Accepted)
laravalidate.BaseRegisterMessages(map[string]laravalidate.MessageResolver{
"accepted": laravalidate.BasicMessageResolver("The :attribute field must be accepted."),
})
// Now you can use the accepted validator
}
There are a lot more methods on the ValidatorCtx
that you can use to get the value of the field.
See the rules.go for examples.