patternpythondjangoTip
Django custom middleware — request/response processing pipeline
Viewed 0 times
Django 3.2+
middlewarerequest processingresponse pipelinecross-cutting concernsprocess_exception
Problem
Cross-cutting concerns like request logging, API key validation, or response compression need to run for all or most views without modifying each view function.
Solution
Write a middleware class with __init__(get_response) and __call__(request). Use process_view() for pre-view logic with access to the view function.
import time
import logging
logger = logging.getLogger(__name__)
class RequestTimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start = time.monotonic()
response = self.get_response(request)
duration = time.monotonic() - start
response['X-Request-Duration'] = f'{duration:.3f}s'
logger.info('%s %s %.3fs', request.method, request.path, duration)
return response
def process_exception(self, request, exception):
logger.error('Unhandled exception: %s', exception, exc_info=True)
return None # Let Django handle itWhy
Django's MIDDLEWARE setting is processed as a chain. Each middleware wraps the next via get_response(). Returning None from process_exception() passes the exception to the next middleware and eventually Django's exception handling.
Gotchas
- Middleware order in settings.MIDDLEWARE matters — SecurityMiddleware should be first
- process_view() runs after URL routing but before the view — access to view_func and view_args
- process_exception() is only called for unhandled exceptions in views, not in other middleware
- Async middleware must use async def __call__ and be compatible with async views (Django 4.1+)
Context
Django projects needing global request/response processing or logging
Revisions (0)
No revisions yet.