Validator Module
Overview
Yokai provides a fxvalidator module, allowing you to inject a validator anywhere needed.
It wraps the go-playground/validator module.
Installation
First install the module:
Then activate it in your application bootstrapper:
package internal
import (
"github.com/ankorstore/yokai/fxcore"
"github.com/ankorstore/yokai/fxvalidator"
)
var Bootstrapper = fxcore.NewBootstrapper().WithOptions(
// modules registration
fxvalidator.FXValidatorModule,
// ...
)
Configuration
Configuration reference:
modules:
validator:
tag_name: validate # struct tag to define validation rules, default = validate
private_fields: false # to enable validation on private fields, disabled by default
Usage
This module makes available a *validator.Validate
instance in
Yokai dependency injection system, that you can inject anywhere.
For example:
package service
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type ExampleStruct struct {
Name string `validate:"required"`
Email string `validate:"required,email"`
}
type ExampleService struct {
validate *validator.Validate
}
func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}
func (s *ExampleService) DoSomething() error {
es := ExampleStruct{
Name: "name",
Email: "name@example.com",
}
err := s.validate.Struct(es)
if valErrs, ok := err.(*validator.ValidationErrors); ok {
fmt.Println(valErrs)
}
return valErrs
}
See go-playground/validator documentation for more details about available validation features.
Customization
This module provides the possibility to easily customize your validator.
Custom aliases
You can register custom validation aliases with AsAlias()
:
package internal
import (
"github.com/ankorstore/yokai/fxvalidator"
"go.uber.org/fx"
)
func Register() fx.Option {
return fx.Options(
// register the rla alias: provided value must be required, alpha only and lowercase
fxvalidator.AsAlias("rla", "required,alpha,lowercase"),
// ...
)
}
Then:
package service
import (
"context"
"github.com/go-playground/validator/v10"
)
type ExampleService struct {
validate *validator.Validate
}
func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}
func (s *ExampleService) DoSomething(ctx context.Context) error {
// valid
err := s.validate.VarCtx(ctx, "valid", "rla")
// invalid
err = s.validate.VarCtx(ctx, "1234", "rla")
return err
}
Custom validations
You can register custom validations functions with AsValidation()
:
package internal
import (
"github.com/ankorstore/yokai/fxvalidator"
"github.com/go-playground/validator/v10"
"go.uber.org/fx"
)
func Register() fx.Option {
return fx.Options(
// register the foobar validation: provided lowercased value must be "foo" or "bar"
fxvalidator.AsValidation(
"foobar",
func(ctx context.Context, fl validator.FieldLevel) bool {
s := strings.ToLower(fl.Field().String())
return s == "foo" || s == "bar"
},
true,
),
// ...
)
}
Then:
package service
import (
"context"
"github.com/go-playground/validator/v10"
)
type ExampleService struct {
validate *validator.Validate
}
func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}
func (s *ExampleService) DoSomething(ctx context.Context) error {
// valid
err := s.validate.VarCtx(ctx, "FoO", "foobar")
// invalid
err = s.validate.VarCtx(ctx, "invalid", "foobar")
return err
}
Custom struct validations
You can register custom struct validations functions with AsStructValidation()
:
package internal
import (
"github.com/ankorstore/yokai/fxvalidator"
"github.com/foo/bar/internal/service"
"github.com/go-playground/validator/v10"
"go.uber.org/fx"
)
func Register() fx.Option {
return fx.Options(
// register a struct validation for the struct service.ExampleStruct{}
fxvalidator.AsStructValidation(
func(ctx context.Context, sl validator.StructLevel) {
fb, ok := sl.Current().Interface().(service.ExampleStruct)
if ok {
if fb.Foo != "foo" {
sl.ReportError(fb.Foo, "Foo", "Foo", "invalid-foo", "invalid foo")
}
if fb.Bar != "bar" {
sl.ReportError(fb.Bar, "Bar", "Bar", "invalid-bar", "invalid bar")
}
}
},
service.ExampleStruct{},
),
// ...
)
}
Then:
package service
import (
"context"
"github.com/go-playground/validator/v10"
)
type ExampleStruct struct {
Foo string
Bar string
}
type ExampleService struct {
validate *validator.Validate
}
func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}
func (s *ExampleService) DoSomething(ctx context.Context) error {
// valid
err := s.validate.StructCtx(ctx, ExampleStruct{Foo: "foo", Bar: "bar"})
// invalid
err = s.validate.StructCtx(ctx, ExampleStruct{Foo: "invalid", Bar: ""})
return err
}
Custom types
You can register custom types functions with AsCustomType()
:
package internal
import (
"reflect"
"github.com/ankorstore/yokai/fxvalidator"
"github.com/foo/bar/internal/service"
"go.uber.org/fx"
)
func Register() fx.Option {
return fx.Options(
// register a custom type function for service.Foo{} and service.Bar{}
fxvalidator.AsCustomType(
func(field reflect.Value) interface{} {
if f, ok := field.Interface().(service.Foo); ok {
if f.Value != "foo" {
return ""
}
return f.Value
}
if b, ok := field.Interface().(service.Bar); ok {
if b.Value != "bar" {
return ""
}
return b.Value
}
return ""
},
service.Foo{},
service.Bar{},
),
// ...
)
}
Then:
package service
import (
"context"
"github.com/go-playground/validator/v10"
)
type Foo struct {
Value string `validate:"required"`
}
type Bar struct {
Value string `validate:"required"`
}
type ExampleStruct struct {
Foo Foo
Bar Bar
}
type ExampleService struct {
validate *validator.Validate
}
func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}
func (s *ExampleService) DoSomething(ctx context.Context) error {
// valid
err := s.validate.StructCtx(ctx, ExampleStruct{Foo: Foo{Value: "foo"}, Bar: Bar{Value: "bar"}})
// invalid
err = s.validate.StructCtx(ctx, ExampleStruct{Foo: Foo{Value: "invalid"}, Bar: Bar{Value: ""}})
return err
}