feat: add tag-based validation system with reflection caching
Implement a declarative struct tag validation system for Go: - Add validator.go with sync.Map caching for reflection metadata - Add rules.go with 11 built-in validation rules (required, email, pattern, honeypot, timing, etc.) - Add errors.go with FieldError and ValidationErrors types - Update ContactFormRequest with validate tags - Add ValidateContactFormV2() using the new tag-based validator Rules implemented: - required/optional: field presence validation - trim/sanitize: automatic value transformations - min/max: UTF-8 aware length validation - email: RFC 5322 email format validation - pattern: predefined regex patterns (name, subject, company) - no_injection: email header injection prevention - honeypot: bot trap (must be empty) - timing: timestamp validation for bot detection Documentation: - docs/go-validation-system.md: complete validation guide - docs/go-template-system.md: template manager documentation - docs/go-routes-api.md: routes and API reference - docs/README.md: documentation index
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package validation
|
||||
|
||||
import "strings"
|
||||
|
||||
// FieldError represents a single field validation error
|
||||
type FieldError struct {
|
||||
Field string `json:"field"` // Field name
|
||||
Tag string `json:"tag"` // Validation tag that failed
|
||||
Param string `json:"param,omitempty"` // Optional parameter (e.g., "100" for "max=100")
|
||||
Message string `json:"message"` // Human-readable error message
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (e FieldError) Error() string {
|
||||
if e.Param != "" {
|
||||
return e.Field + ": " + e.Message + " (" + e.Tag + "=" + e.Param + ")"
|
||||
}
|
||||
return e.Field + ": " + e.Message
|
||||
}
|
||||
|
||||
// ValidationErrors represents multiple field validation errors
|
||||
type ValidationErrors []FieldError
|
||||
|
||||
// Error implements the error interface
|
||||
func (ve ValidationErrors) Error() string {
|
||||
if len(ve) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("validation failed: ")
|
||||
for i, err := range ve {
|
||||
if i > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString(err.Error())
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// HasErrors returns true if there are any validation errors
|
||||
func (ve ValidationErrors) HasErrors() bool {
|
||||
return len(ve) > 0
|
||||
}
|
||||
|
||||
// GetFieldError returns the first error for a specific field, or nil if none
|
||||
func (ve ValidationErrors) GetFieldError(field string) *FieldError {
|
||||
for _, err := range ve {
|
||||
if err.Field == field {
|
||||
return &err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFieldErrors returns all errors for a specific field
|
||||
func (ve ValidationErrors) GetFieldErrors(field string) []FieldError {
|
||||
var errors []FieldError
|
||||
for _, err := range ve {
|
||||
if err.Field == field {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
Reference in New Issue
Block a user