Files
aitbc/docs/development/validation-patterns.md
aitbc 56d510e75f Add request validation and error handling middleware
- Created RequestValidationMiddleware for request/response size validation
- Created ErrorHandlerMiddleware for standardized error responses
- Added both middlewares to FastAPI app
- Created validation patterns documentation
- Configured 10MB default size limits for requests and responses

This completes Phase 6: Request Validation Middleware
2026-04-30 11:08:39 +02:00

4.8 KiB

Request Validation Patterns

Overview

This document describes the request validation patterns and middleware for AITBC services.

Middleware Stack

The coordinator-api uses the following middleware stack (in order of execution):

  1. CORSMiddleware - CORS handling
  2. RequestIDMiddleware - Request ID correlation
  3. PerformanceLoggingMiddleware - Performance tracking
  4. RequestValidationMiddleware - Request/response size validation
  5. ErrorHandlerMiddleware - Standardized error responses

Request Validation Middleware

Purpose

Validates incoming and outgoing requests/responses to ensure they meet size limits and other constraints.

Configuration

app.add_middleware(
    RequestValidationMiddleware,
    max_request_size=10*1024*1024,  # 10MB
)

Validation Rules

  • Request size: Maximum 10MB by default
  • Response size: Maximum 10MB by default
  • Content-Length header: Must be valid integer if present

Error Responses

If validation fails, returns HTTP 413 (Payload Too Large):

{
  "detail": "Request too large. Maximum size is 10485760 bytes"
}

Error Handler Middleware

Purpose

Standardizes error responses across all endpoints with consistent format and logging.

Error Response Format

All errors are returned in the following format:

{
  "error": {
    "type": "http_error | internal_error",
    "message": "Error description",
    "status_code": 400 | 500,
    "path": "/api/endpoint"
  }
}

Error Types

  • http_error: HTTP exceptions (4xx errors)
  • internal_error: Unhandled exceptions (5xx errors)

Logging

All errors are logged with context:

  • HTTP exceptions: WARNING level
  • Internal exceptions: ERROR level with stack trace

Request ID Correlation

Purpose

Adds a unique request ID to each request for correlation across distributed systems.

Implementation

  • Generates or retrieves request ID from X-Request-ID header
  • Binds request ID to logger context
  • Adds request ID to response headers
  • Logs request start and completion

Usage

request_id = request.state.request_id
logger = logger.bind(request_id=request_id)

Performance Logging

Purpose

Tracks request timing and performance metrics.

Implementation

  • Logs request duration in milliseconds
  • Adds X-Process-Time header to responses
  • Logs method, path, and status code

Response Header

X-Process-Time: 0.123

Validation Guidelines

DO

  • Use Pydantic models for request body validation
  • Set appropriate size limits for your use case
  • Log validation failures with context
  • Return standardized error responses
  • Include request ID in error logs

DON'T

  • Disable validation in production
  • Allow unbounded request sizes
  • Return raw exceptions to clients
  • Log sensitive information in error messages
  • Skip error logging

Example: Adding Custom Validation

from fastapi import Request, HTTPException
from pydantic import BaseModel, Field, validator

class CreateUserRequest(BaseModel):
    username: str = Field(..., min_length=3, max_length=50)
    email: str = Field(..., regex=r'^[^@]+@[^@]+\.[^@]+$')
    
    @validator('username')
    def validate_username(cls, v):
        if not v.isalnum():
            raise ValueError('Username must be alphanumeric')
        return v

@router.post("/users")
async def create_user(request: CreateUserRequest):
    # Request is automatically validated by FastAPI
    return {"status": "created", "username": request.username}

Rate Limiting

Rate limiting is already implemented using slowapi:

from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@router.post("/endpoint")
@limiter.limit("10/minute")
async def endpoint(request: Request):
    return {"status": "ok"}

Configuration

Middleware can be configured in main.py:

# Request validation
app.add_middleware(
    RequestValidationMiddleware,
    max_request_size=10*1024*1024,  # 10MB
    max_response_size=10*1024*1024,  # 10MB
)

# Error handling
app.add_middleware(ErrorHandlerMiddleware)

Testing

Test validation middleware:

from fastapi.testclient import TestClient

client = TestClient(app)

# Test request size validation
response = client.post(
    "/endpoint",
    data="x" * 11 * 1024 * 1024,  # 11MB
)
assert response.status_code == 413

# Test error handling
response = client.get("/nonexistent")
assert response.status_code == 404
assert "error" in response.json()

Security Considerations

  • Request size limits prevent DoS attacks
  • Response size limits prevent memory exhaustion
  • Error responses don't leak sensitive information
  • Request IDs enable security audit trails
  • All validation failures are logged for monitoring