Context-Aware Validation
Govalid generates context-aware validation methods for every struct.
Enabling request-scoped validation with cancellation and timeout handling.
Generated Methods
For each struct with validation markers, four methods are generated:
1
2
3
4
5
6
7
| func ValidatePersonRequest(t *PersonRequest) error
func ValidatePersonRequestContext(ctx context.Context, t *PersonRequest) error
func (t *PersonRequest) Validate() error
func (t *PersonRequest) ValidateContext(ctx context.Context) error
|
The Validate() and ValidatePersonRequest() methods delegate to their context aware equivalents using context.Background().
How Context Cancellation Works
Context cancellation is checked between field validations.
Generated code follows this pattern:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| func ValidatePersonRequestContext(ctx context.Context, t *PersonRequest) error {
if t == nil {
return ErrNilPersonRequest
}
var errs govaliderrors.ValidationErrors
if ctx.Err() != nil {
return ctx.Err()
}
if t.Name == "" {
err := ErrPersonRequestNameRequiredValidation
err.Value = t.Name
errs = append(errs, err)
}
if ctx.Err() != nil {
return ctx.Err()
}
if t.Email == "" {
err := ErrPersonRequestEmailRequiredValidation
err.Value = t.Email
errs = append(errs, err)
}
if !validationhelper.IsValidEmail(t.Email) {
err := ErrPersonRequestEmailEmailValidation
err.Value = t.Email
errs = append(errs, err)
}
if len(errs) > 0 {
return errs
}
return nil
}
|
Each ctx.Err() check provides a cancellation point before the next field’s validation.
Usage Examples
With Timeout
1
2
3
4
5
6
7
| ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
err := ValidatePersonRequestContext(ctx, &person)
if err == context.DeadlineExceeded {
log.Println("Validation timed out")
}
|
With Cancellation
1
2
3
4
5
6
7
8
9
10
11
| ctx, cancel := context.WithCancel(context.Background())
go func() {
err := ValidatePersonRequestContext(ctx, &person)
if err == context.Canceled {
log.Println("Validation cancelled")
}
}()
// Cancel from another goroutine
cancel()
|
HTTP Handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| func createPersonHandler(w http.ResponseWriter, r *http.Request) {
var req PersonRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if err := req.ValidateContext(r.Context()); err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
http.Error(w, "Request cancelled", http.StatusRequestTimeout)
return
}
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Process valid request...
}
|
HTTP Middleware
govalid provides middleware for automatic request validation:
1
2
3
4
5
6
7
| import "github.com/sivchari/govalid/validation/middleware"
// Standard validation (no context awareness)
http.HandleFunc("/users", middleware.ValidateRequest[PersonRequest](handler))
// Context-aware validation (honors request cancellation)
http.HandleFunc("/users", middleware.ValidateRequestContext[PersonRequest](handler))
|
ValidateRequest[T] - Calls Validate(), ignores request contextValidateRequestContext[T] - Calls ValidateContext(r.Context()), respects client cancellation
Migration
Existing code requires no changes. To add context support:
1
2
3
4
5
6
7
| // Before
err := ValidatePersonRequest(&person)
// After (with context)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err := ValidatePersonRequestContext(ctx, &person)
|