Django Middleware

Processing requests and responses globally

🔄 What is Django Middleware?

Middleware is a framework of hooks that process requests and responses globally before they reach views or after they leave. It's like a security checkpoint that every request passes through.


# Simple middleware example
class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # Code before view
        response = self.get_response(request)
        # Code after view
        return response
                                    

Key Middleware Concepts

🔐

Security

Protect your application from attacks

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
]
👤

Authentication

Handle user sessions and authentication

MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
]
🌐

CORS

Enable cross-origin resource sharing

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
]
📝

Logging

Track requests and responses

class LoggingMiddleware:
    def __call__(self, request):
        print(f"Request: {request.path}")
        return self.get_response(request)

🔹 Creating Custom Middleware

Middleware processes every request and response in your Django application. It's useful for logging, authentication checks, or modifying requests globally. Here's how to create a simple custom middleware that adds a custom header to every response.

# middleware.py
class CustomHeaderMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # Process request before view
        print(f"Processing: {request.path}")
        
        # Get response from view
        response = self.get_response(request)
        
        # Modify response after view
        response['X-Custom-Header'] = 'Hello from Middleware'
        
        return response

Explanation:

__init__: Initializes middleware once when server starts

__call__: Runs for every request

get_response: Calls the next middleware or view

🔹 Registering Middleware

Add your middleware to settings.py in the MIDDLEWARE list. Order matters - middleware executes top to bottom for requests and bottom to top for responses.

# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'myapp.middleware.CustomHeaderMiddleware',  # Your custom middleware
]

🔹 Middleware with Process Methods

Advanced middleware can hook into different stages of request/response processing using special methods for more control over the request lifecycle.

class AdvancedMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        return self.get_response(request)
    
    def process_view(self, request, view_func, view_args, view_kwargs):
        # Called before view executes
        print(f"View: {view_func.__name__}")
        return None
    
    def process_exception(self, request, exception):
        # Called when view raises exception
        print(f"Error: {exception}")
        return None
    
    def process_template_response(self, request, response):
        # Called after view returns TemplateResponse
        response.context_data['extra'] = 'Added by middleware'
        return response

Hook Methods:

process_view: Runs before the view function

process_exception: Handles exceptions from views

process_template_response: Modifies template responses

🔹 Practical Example: Request Timer

This middleware measures how long each request takes to process, useful for performance monitoring and optimization.

import time

class RequestTimerMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # Start timer
        start_time = time.time()
        
        # Process request
        response = self.get_response(request)
        
        # Calculate duration
        duration = time.time() - start_time
        
        # Add to response header
        response['X-Request-Duration'] = f"{duration:.3f}s"
        
        print(f"{request.path} took {duration:.3f} seconds")
        
        return response

Output Example:

/home/ took 0.045 seconds

/api/users/ took 0.123 seconds

/admin/ took 0.089 seconds

🧠 Test Your Knowledge

What does middleware process in Django?