- Spec files
pypi-fastapi
Describes: pkg:pypi/fastapi@0.116.x
- Description
- FastAPI framework, high performance, easy to learn, fast to code, ready for production
- Author
- tessl
- Last updated
middleware.md docs/
1# Middleware23FastAPI provides comprehensive middleware support for processing HTTP requests and responses. Middleware components can modify requests before they reach route handlers and modify responses before they're sent to clients. FastAPI includes built-in middleware classes and supports custom middleware development.45## Capabilities67### Base Middleware Class89Base class for creating custom middleware components that process requests and responses.1011```python { .api }12class Middleware:13def __init__(self, cls: type, **options: Any) -> None:14"""15Base middleware class for custom middleware.1617Parameters:18- cls: Middleware class to instantiate19- options: Configuration options for the middleware20"""21self.cls = cls22self.options = options23```2425### Middleware Decorator2627Decorator method on FastAPI and APIRouter instances for adding middleware functions.2829```python { .api }30def middleware(self, middleware_type: str) -> Callable[[Callable], Callable]:31"""32Decorator for adding middleware to the application.3334Parameters:35- middleware_type: Type of middleware ("http" for HTTP middleware)3637Returns:38Decorator function for middleware registration39"""40```4142### CORS Middleware4344Cross-Origin Resource Sharing middleware for handling cross-domain requests.4546```python { .api }47class CORSMiddleware:48def __init__(49self,50app: ASGIApp,51allow_origins: List[str] = None,52allow_methods: List[str] = None,53allow_headers: List[str] = None,54allow_credentials: bool = False,55allow_origin_regex: str = None,56expose_headers: List[str] = None,57max_age: int = 60058) -> None:59"""60Cross-Origin Resource Sharing middleware.6162Parameters:63- app: ASGI application to wrap64- allow_origins: List of allowed origin URLs65- allow_methods: List of allowed HTTP methods66- allow_headers: List of allowed request headers67- allow_credentials: Allow credentials in cross-origin requests68- allow_origin_regex: Regex pattern for allowed origins69- expose_headers: List of headers to expose to the browser70- max_age: Maximum age for preflight cache71"""7273async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:74"""Process ASGI request with CORS handling."""75```7677### GZip Middleware7879Middleware for compressing HTTP responses using GZip compression.8081```python { .api }82class GZipMiddleware:83def __init__(84self,85app: ASGIApp,86minimum_size: int = 500,87compresslevel: int = 988) -> None:89"""90GZip compression middleware.9192Parameters:93- app: ASGI application to wrap94- minimum_size: Minimum response size to compress (bytes)95- compresslevel: GZip compression level (1-9)96"""9798async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:99"""Process ASGI request with GZip compression."""100```101102### HTTPS Redirect Middleware103104Middleware for enforcing HTTPS connections by redirecting HTTP requests.105106```python { .api }107class HTTPSRedirectMiddleware:108def __init__(self, app: ASGIApp) -> None:109"""110HTTPS redirect enforcement middleware.111112Parameters:113- app: ASGI application to wrap114"""115116async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:117"""Process ASGI request with HTTPS redirect."""118```119120### Trusted Host Middleware121122Middleware for validating Host headers to prevent Host header attacks.123124```python { .api }125class TrustedHostMiddleware:126def __init__(127self,128app: ASGIApp,129allowed_hosts: List[str] = None,130www_redirect: bool = True131) -> None:132"""133Trusted host validation middleware.134135Parameters:136- app: ASGI application to wrap137- allowed_hosts: List of allowed host patterns138- www_redirect: Redirect www subdomain to non-www139"""140141async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:142"""Process ASGI request with host validation."""143```144145### WSGI Middleware146147Middleware for mounting WSGI applications within ASGI applications.148149```python { .api }150class WSGIMiddleware:151def __init__(self, app: ASGIApp, wsgi_app: WSGIApp) -> None:152"""153WSGI application mounting middleware.154155Parameters:156- app: ASGI application to wrap157- wsgi_app: WSGI application to mount158"""159160async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:161"""Process ASGI request with WSGI app mounting."""162```163164### Custom Middleware Interface165166Interface for creating custom HTTP middleware functions.167168```python { .api }169async def custom_middleware_function(170request: Request,171call_next: Callable[[Request], Awaitable[Response]]172) -> Response:173"""174Custom middleware function signature.175176Parameters:177- request: HTTP request object178- call_next: Function to call next middleware/route handler179180Returns:181Response object (potentially modified)182"""183```184185## Usage Examples186187### Basic Custom Middleware188189```python190import time191from fastapi import FastAPI, Request192193app = FastAPI()194195@app.middleware("http")196async def add_process_time_header(request: Request, call_next):197start_time = time.time()198response = await call_next(request)199process_time = time.time() - start_time200response.headers["X-Process-Time"] = str(process_time)201return response202203@app.get("/")204async def read_main():205return {"message": "Hello World"}206```207208### CORS Middleware Configuration209210```python211from fastapi import FastAPI212from fastapi.middleware.cors import CORSMiddleware213214app = FastAPI()215216app.add_middleware(217CORSMiddleware,218allow_origins=["http://localhost:3000", "https://myapp.com"],219allow_credentials=True,220allow_methods=["GET", "POST", "PUT", "DELETE"],221allow_headers=["*"],222expose_headers=["X-Custom-Header"],223max_age=600224)225226@app.get("/api/data")227async def get_data():228return {"data": "This endpoint supports CORS"}229```230231### GZip Compression Middleware232233```python234from fastapi import FastAPI235from fastapi.middleware.gzip import GZipMiddleware236237app = FastAPI()238239# Enable GZip compression for responses larger than 1000 bytes240app.add_middleware(GZipMiddleware, minimum_size=1000)241242@app.get("/large-data")243async def get_large_data():244# Return large response that will be compressed245return {"data": "x" * 2000, "message": "This response will be compressed"}246```247248### HTTPS Redirect Middleware249250```python251from fastapi import FastAPI252from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware253254app = FastAPI()255256# Redirect all HTTP requests to HTTPS257app.add_middleware(HTTPSRedirectMiddleware)258259@app.get("/secure-endpoint")260async def secure_endpoint():261return {"message": "This endpoint requires HTTPS"}262```263264### Trusted Host Middleware265266```python267from fastapi import FastAPI268from fastapi.middleware.trustedhost import TrustedHostMiddleware269270app = FastAPI()271272# Only allow requests from specific hosts273app.add_middleware(274TrustedHostMiddleware,275allowed_hosts=["example.com", "*.example.com", "localhost"]276)277278@app.get("/")279async def read_main():280return {"message": "Request from trusted host"}281```282283### Authentication Middleware284285```python286import jwt287from fastapi import FastAPI, Request, HTTPException288from fastapi.responses import JSONResponse289290app = FastAPI()291292SECRET_KEY = "your-secret-key"293ALGORITHM = "HS256"294295@app.middleware("http")296async def authenticate_request(request: Request, call_next):297# Skip authentication for certain paths298if request.url.path in ["/login", "/docs", "/openapi.json"]:299response = await call_next(request)300return response301302# Extract token from Authorization header303auth_header = request.headers.get("Authorization")304if not auth_header or not auth_header.startswith("Bearer "):305return JSONResponse(306status_code=401,307content={"error": "Missing or invalid authorization header"}308)309310token = auth_header.replace("Bearer ", "")311312try:313# Verify JWT token314payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])315request.state.user = payload316except jwt.InvalidTokenError:317return JSONResponse(318status_code=401,319content={"error": "Invalid token"}320)321322response = await call_next(request)323return response324325@app.get("/protected")326async def protected_route(request: Request):327return {"message": f"Hello {request.state.user['sub']}"}328```329330### Logging Middleware331332```python333import logging334import time335from fastapi import FastAPI, Request336337# Configure logging338logging.basicConfig(level=logging.INFO)339logger = logging.getLogger(__name__)340341app = FastAPI()342343@app.middleware("http")344async def log_requests(request: Request, call_next):345start_time = time.time()346347# Log request348logger.info(349f"Request: {request.method} {request.url.path}",350extra={351"method": request.method,352"path": request.url.path,353"query_params": str(request.query_params),354"client": request.client.host if request.client else None355}356)357358# Process request359response = await call_next(request)360361# Log response362process_time = time.time() - start_time363logger.info(364f"Response: {response.status_code} in {process_time:.4f}s",365extra={366"status_code": response.status_code,367"process_time": process_time,368"path": request.url.path369}370)371372return response373374@app.get("/")375async def read_main():376return {"message": "Hello World"}377```378379### Rate Limiting Middleware380381```python382import time383from collections import defaultdict384from fastapi import FastAPI, Request, HTTPException385386app = FastAPI()387388# Simple in-memory rate limiter389rate_limiter = defaultdict(list)390RATE_LIMIT = 10 # requests per minute391RATE_WINDOW = 60 # seconds392393@app.middleware("http")394async def rate_limit_middleware(request: Request, call_next):395client_ip = request.client.host if request.client else "unknown"396current_time = time.time()397398# Clean old requests399rate_limiter[client_ip] = [400req_time for req_time in rate_limiter[client_ip]401if current_time - req_time < RATE_WINDOW402]403404# Check rate limit405if len(rate_limiter[client_ip]) >= RATE_LIMIT:406raise HTTPException(407status_code=429,408detail="Rate limit exceeded",409headers={"Retry-After": str(RATE_WINDOW)}410)411412# Add current request413rate_limiter[client_ip].append(current_time)414415response = await call_next(request)416return response417418@app.get("/")419async def read_main():420return {"message": "Hello World"}421```422423### Error Handling Middleware424425```python426import traceback427from fastapi import FastAPI, Request, HTTPException428from fastapi.responses import JSONResponse429430app = FastAPI()431432@app.middleware("http")433async def catch_exceptions_middleware(request: Request, call_next):434try:435response = await call_next(request)436return response437except HTTPException:438# Re-raise HTTPExceptions to be handled by FastAPI439raise440except Exception as e:441# Handle unexpected exceptions442error_id = str(hash(str(e) + str(time.time())))443444# Log the full traceback445logger.error(446f"Unhandled exception {error_id}: {str(e)}",447extra={448"error_id": error_id,449"path": request.url.path,450"method": request.method,451"traceback": traceback.format_exc()452}453)454455return JSONResponse(456status_code=500,457content={458"error": "Internal server error",459"error_id": error_id,460"message": "An unexpected error occurred"461}462)463464@app.get("/error")465async def trigger_error():466raise ValueError("This is a test error")467468@app.get("/")469async def read_main():470return {"message": "Hello World"}471```472473### Multiple Middleware Stack474475```python476from fastapi import FastAPI, Request477from fastapi.middleware.cors import CORSMiddleware478from fastapi.middleware.gzip import GZipMiddleware479import time480481app = FastAPI()482483# Add multiple middleware in order484# Note: Middleware is executed in reverse order of addition485486# 1. GZip (executed last - compresses final response)487app.add_middleware(GZipMiddleware, minimum_size=1000)488489# 2. CORS (executed second to last)490app.add_middleware(491CORSMiddleware,492allow_origins=["*"],493allow_credentials=True,494allow_methods=["*"],495allow_headers=["*"],496)497498# 3. Custom timing middleware (executed first)499@app.middleware("http")500async def add_timing_header(request: Request, call_next):501start_time = time.time()502response = await call_next(request)503process_time = time.time() - start_time504response.headers["X-Process-Time"] = str(process_time)505return response506507@app.get("/")508async def read_main():509return {"message": "Hello World", "data": "x" * 1500} # Large response for GZip510```511512### Conditional Middleware513514```python515from fastapi import FastAPI, Request516import os517518app = FastAPI()519520# Only add CORS middleware in development521if os.getenv("ENVIRONMENT") == "development":522from fastapi.middleware.cors import CORSMiddleware523app.add_middleware(524CORSMiddleware,525allow_origins=["*"],526allow_credentials=True,527allow_methods=["*"],528allow_headers=["*"],529)530531# Security middleware for production532if os.getenv("ENVIRONMENT") == "production":533from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware534from fastapi.middleware.trustedhost import TrustedHostMiddleware535536app.add_middleware(HTTPSRedirectMiddleware)537app.add_middleware(538TrustedHostMiddleware,539allowed_hosts=["myapp.com", "*.myapp.com"]540)541542@app.middleware("http")543async def environment_header(request: Request, call_next):544response = await call_next(request)545response.headers["X-Environment"] = os.getenv("ENVIRONMENT", "unknown")546return response547548@app.get("/")549async def read_main():550return {"message": "Hello World"}551```552553### Custom Middleware Class554555```python556from fastapi import FastAPI, Request, Response557from starlette.middleware.base import BaseHTTPMiddleware558import uuid559560class RequestIDMiddleware(BaseHTTPMiddleware):561async def dispatch(self, request: Request, call_next):562# Generate unique request ID563request_id = str(uuid.uuid4())564565# Add request ID to request state566request.state.request_id = request_id567568# Process request569response = await call_next(request)570571# Add request ID to response headers572response.headers["X-Request-ID"] = request_id573574return response575576app = FastAPI()577578# Add custom middleware class579app.add_middleware(RequestIDMiddleware)580581@app.get("/")582async def read_main(request: Request):583return {584"message": "Hello World",585"request_id": request.state.request_id586}587```