Custom Request Validation with ozzo-validation in Go
Custom validation in Echo using ozzo-validation makes input handling much cleaner and more structured. Instead of manually checking each field, you can define validation rules declaratively. Let’s say we need to validate a form submission where each field has attributes like label, type, and is_required. package form_requests import ( "fmt" "regexp" validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/labstack/echo/v4" ) // FieldRequest defines a single form field type FieldRequest struct { Label string `json:"label"` Type string `json:"type"` IsRequired bool `json:"is_required"` } // CreateFormFieldsRequest wraps multiple fields type CreateFormFieldsRequest struct { Fields []FieldRequest `json:"fields"` } // BindAndValidate binds JSON data and runs validation func (r *CreateFormFieldsRequest) BindAndValidate(c echo.Context) []string { if err := c.Bind(r); err != nil { return []string{"Invalid request payload"} } err := validation.ValidateStruct(r, validation.Field(&r.Fields, validation.Required.Error("Fields array is required"), validation.Each( validation.By(validateField), )), ) return extractValidationErrors(err) } // validateField ensures each field meets the required rules func validateField(value interface{}) error { field, ok := value.(FieldRequest) if !ok { return fmt.Errorf("invalid field format") } return validation.ValidateStruct(&field, validation.Field(&field.Label, validation.Required.Error("Label is required"), validation.Length(3, 50).Error("Label must be between 3 and 50 characters"), validation.Match(regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)).Error("Label can only contain letters, numbers, underscores, and dashes"), ), validation.Field(&field.Type, validation.Required.Error("Type is required"), validation.In("text", "number", "email", "date", "checkbox").Error("Invalid field type"), ), validation.Field(&field.IsRequired, validation.Required.Error("IsRequired is required")), ) } // extractValidationErrors formats errors into a slice of strings func extractValidationErrors(err error) []string { if err == nil { return nil } var errors []string if ve, ok := err.(validation.Errors); ok { for _, e := range ve { errors = append(errors, e.Error()) } } return errors } Declarative Validation: Instead of manually checking fields, ozzo-validation provides a clean way to define rules. Custom Validation Function: validateField() ensures each field meets the required format and constraints. Consistent Error Handling: extractValidationErrors() collects and formats validation errors into a slice for easier debugging and response handling.