Django middleware is a powerful tool that allows you to process requests and responses globally across your entire Django application. In this post, we'll explore what middleware is, how it works, and walk through some practical examples.
What is Django Middleware?
Middleware in Django is a framework of hooks into Django's request/response processing. It's a lightweight, low-level plugin system for globally altering Django's input or output.
Each middleware component is responsible for doing some specific function. For example, Django includes a middleware that adds the CSRF token to responses and another that handles user sessions.
How Does Middleware Work?
Middleware classes are called in the order they're defined in the MIDDLEWARE
setting. During the request phase, Django calls the process_request()
method of each middleware in order. During the response phase, the process_response()
methods are called in reverse order.
Here's a simplified view of the request/response cycle:
Request comes in
Middleware 1 processes request
Middleware 2 processes request
View processes request and produces response
Middleware 2 processes response
Middleware 1 processes response
Response goes out
Creating Custom Middleware
Let's look at some examples of custom middleware:
Example 1: Timing Middleware
This middleware will measure how long each request takes to process:
import time
class TimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start_time = time.time()
response = self.get_response(request)
duration = time.time() - start_time
response['X-Page-Generation-Duration-ms'] = int(duration * 1000)
return response
This middleware adds an X-Page-Generation-Duration-ms
header to each response, showing how many milliseconds the request took to process.
Example 2: IP Restriction Middleware
This middleware will restrict access to certain views based on IP address:
from django.http import HttpResponseForbidden
class IPRestrictionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
allowed_ips = ['192.168.1.1', '192.168.1.2']
ip = request.META.get('REMOTE_ADDR')
if request.path.startswith('/admin/') and ip not in allowed_ips:
return HttpResponseForbidden("You are not allowed to access this resource.")
return self.get_response(request)
This middleware checks if the request is for the admin area and if so, verifies that the client's IP is in the allowed list.
Example 3: Request Logging Middleware
This middleware will log details about each request:
import logging
logger = logging.getLogger(__name__)
class RequestLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
logger.info(f"Request: {request.method} {request.path} from {request.META.get('REMOTE_ADDR')}")
response = self.get_response(request)
logger.info(f"Response: {response.status_code}")
return response
This middleware logs the HTTP method, path, and IP address for each request, as well as the status code of each response.
Using Your Middleware
To use your custom middleware, add it to the MIDDLEWARE
setting in your Django settings file:
MIDDLEWARE = [
# ... other middleware classes ...
'path.to.TimingMiddleware',
'path.to.IPRestrictionMiddleware',
'path.to.RequestLoggingMiddleware',
]
Remember, the order matters! Middleware classes are processed in the order they appear in this list.
Conclusion
Middleware is a powerful feature in Django that allows you to process requests and responses globally. Whether you're adding security features, logging information, or modifying responses, middleware provides a clean and reusable way to add functionality to your Django application.
By creating custom middleware, you can keep your views clean and focused on their primary logic, while handling cross-cutting concerns at the application level.