6c7595b041
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
66 lines
1.6 KiB
Go
66 lines
1.6 KiB
Go
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
|
|
}
|