Use Django middleware to modify requests & responses

Problem

You want to add, remove or update content from requests or responses before or after they cross into view methods or templates.

Solution

Create a Python class with the various methods supported by Django middleware: __init__, process_request(), process_view(), process_response(), process_exception() and process_template_response(). Define the Python class as part of the MIDDLEWARE_CLASSES variable in your Django project's settings.py.

How it works

In most circumstances you add, remove or update content from requests or responses directly in Django view methods. However, sometimes it's convenient to apply such modifications across all requests and responses, in which case you benefit greatly from using Django middleware.

Django middleware is composed as Python classes. You can think of Django middleware as Python classes that are executed on all requests and responses that occur in an application. However, before exploring the structure required for these Django middleware Python classes, we'll take a brief detour to look at Django's built-in middleware classes to know what's already available and get a better understanding of how Django middleware works.

Django built-in middleware classes

Django comes equipped with a series of middleware classes, some of which are enabled by default on all Django projects. If you open a Django project's settings.py file you'll notice the MIDDLEWARE_CLASSES variable. The MIDDLEWARE_CLASSES variable is assigned a tuple of Python classes that operate as Django middleware. Listing 1 illustrates the default contents of the MIDDLEWARE_CLASSES variable.

Listing 1 - Default Django middleware classes in MIDDLEWARE_CLASSES


MIDDLEWARE_CLASSES = (
    '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',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

As you can see in listing 1, Django in its out-of-the-box state already comes enabled with seven middleware classes, so all requests and responses are already running through these seven classes. If you plan to leverage Django's main features, I would advise you not to remove any of these default middleware classes. However, you could leave the MIDDLEWARE_CLASSES variable empty if you wish, just be aware you may break certain Django functionalities in the process.

To give you a better understanding of what the Django middleware classes in listing 1 do and help you make a more informed decision to disable them or not, table 1 describes the functionality for each of these middleware classes.

Table 1 - Django default middleware classes and functionality
Middleware classFunctionality
django.middleware.security.SecurityMiddlewareProvides several security enhancements to the request/response cycle.
django.contrib.sessions.middleware.SessionMiddlewareEnables session support.
django.middleware.common.CommonMiddlewareProvides a common set of features, that include:
  • Forbids access to user agents in the DISALLOWED_USER_AGENTS setting, which can be a list of compiled regular expression objects.
  • Performs URL rewriting based on the APPEND_SLASH and PREPEND_WWW settings in order to normalize URLs.
  • Handles ETags based on the USE_ETAGS.
django.middleware.csrf.CsrfViewMiddlewareAdds protection against Cross Site Request Forgeries by adding hidden form fields to POST forms and checking requests for the correct value.
django.contrib.auth.middleware.AuthenticationMiddlewareAdds the user attribute, representing the currently-logged-in user, to every incoming HttpRequest object. NOTE: This middleware class depends on functionality from the middleware django.contrib.sessions.middleware. SessionMiddleware and must appear after it.
django.contrib.messages.middleware.MessageMiddlewareEnables cookie-based and session-based message support. NOTE: This middleware class depends on functionality from the middleware django.contrib.sessions.middleware. SessionMiddleware and must appear after it.
django.middleware.clickjacking.XFrameOptionsMiddlewareProvides clickjacking protection via the X-Frame-Options header. For more details on what is clickjacking see: http://en.wikipedia.org/wiki/Clickjacking.

As you can see in table 1, although the purpose of the various default middleware classes varies considerably, their functionality applies to features that need to be applied across all requests and responses. Another important factor of the middleware classes in table 1 is that some are dependent on others. For example, the AuthenticationMiddleware class is designed on the assumption it will have access to functionality provided by the SessionMiddleware class. Such dependencies are important because it makes the class definition order relevant (i.e. certain middleware classes need to be defined before others in MIDDLEWARE_CLASSES) a topic I'll elaborate on more in the next section.

In addition to the default middleware classes presented in table 1, Django also includes other middleware classes. Table 2 illustrates the remaining set of Django middleware classes you can leverage in your projects, which can be helpful so you don't have to write all your middleware classes.

Table 2 - Other Django middleware classes and functionality
Middleware classFunctionality
django.middleware.cache.UpdateCacheMiddlewareResponse-phase cache middleware that updates the cache if the response is cacheable.NOTE: UpdateCacheMiddleware must be the first piece of middleware in MIDDLEWARE_CLASSES so that it'll get called last during the response phase.
django.middleware.cache.FetchFromCacheMiddlewareRequest-phase cache middleware that fetches a page from the cache.NOTE:FetchFromCacheMiddleware must be the last piece of middleware in MIDDLEWARE_CLASSES so that it'll get called last during the request phase.
django.middleware.common.BrokenLinkEmailsMiddlewareSends broken link notification emails to MANAGERS.
django.middleware.gzip.GZipMiddlewareCompresses content for browsers that understand GZip compression, which is all modern browsers. NOTE: GZipMiddleware should be placed before any other middleware that need to read or write the response body so that compression happens afterward.
django.middleware.http.ConditionalGetMiddlewareHandles conditional GET operations. If the response has an ETag or Last-Modified header, and the request has If-None-Match or If-Modified-Since, the response is replaced by an HttpNotModified.
django.middleware.locale.LocaleMiddlewareParses a request and decides what translation object to install in the current thread context. This allows pages to be dynamically translated to the language the user desires.
django.contrib.sites.middleware.CurrentSiteMiddlewareAdds the site attribute representing the current site to every incoming HttpRequest object.
django.contrib.auth.middleware.
PersistentRemoteUserMiddleware
Adds REMOTE_USER -- available in request.META -- via an external source (e.g. web server) for the purpose of Django authentication.
django.contrib.auth.middleware.RemoteUserMiddlewareAllows web-server-provided authentication.If request.user is not authenticated, this middleware attempts to authenticate the username passed in the REMOTE_USER request header. If authentication is successful, the user is automatically logged in to persist the user in the session.
django.contrib.flatpages.middleware.FlatpageFallbackMiddlewareEach time a Django application raises a 404 error, this middleware checks the flatpages database for the requested URL as a last resort.
django.contrib.redirects.middleware.RedirectFallbackMiddlewareEach time a Django application raises a 404 error, this middleware checks the redirects database for the requested URL as a last resort.

Now that you know about Django's built-in middleware classes and what it's they are generally used for, lets take a look at the structure of these middleware classes as well as their execution process.

Django middleware class methods and execution process

A Django middleware class can have five callback methods that execute at different points of the request/response life-cycle. Two of these callback methods run in the request phase, while the other three callback methods run in the response phase. The following list provides a brief description of these methods:

Note Request phase executes classes from top to bottom & response phase executes classes from bottom to top.

Middleware class methods that run in the request phase and response phase have an important execution order that is particularly important if you rely on one middleware class to set up something for another middleware class.

Methods in the request phase (i.e. process_request() and process_view()) run from the top to the bottom class definitions in MIDDLEWARE_CLASSES. Based on listing 1, this means process_request for the SecurityMiddleware class is executed first, then for the SessionMiddleware class and last for the XFrameOptionsMiddleware class.

Methods in the response phase (i.e. process_template_response(),process_response() and process_exception()) run from the bottom to the top class definitions in MIDDLEWARE_CLASSES. Based on listing 1, this means process_response for the XFrameOptionsMiddleware class is executed first, then for the MessageMiddleware class and last for the SecurityMiddleware class.

Visually the execution process for the default Django middleware classes in listing 1 is illustrated in figure 1.

Django middleware execution process
Figure 1.- Django middleware execution process

Now that you understand the execution process of Django middleware classes as well as the different methods available in a Django middleware class, lets explore the actual Django middleware class methods in depth.

Django middleware class methods input and output

In this section, I'll split a custom Django middleware class into different pieces so you can get a better understanding of the input and output required for each method.

Listing 2 illustrates the top level class definition -- CoffeehouseMiddleware -- which would need to be declared as part of the MIDDLEWARE_CLASSES definition, as well as the __init__ method used to initialize the middleware class.

Listing 2 - Django midddleware class definition and __init__ method


class CoffeehouseMiddleware(object):

    def __init__(self):        
        """ Global state can be set in Python __init__ method
	  NOTE: Django initializes middleware without arguments, so you can't define __init__ with arguments.
	  NOTE2: Unlike process_* methods that get called once per request, __init__ gets called only once, when the Web server responds to the first request.
        """
        pass

Listing 3 illustrates the process_request method which is the first overall method to run in a Django middleware class as part of the request phase.

Listing 3 - Django midddleware class process_request method

    def process_request(self, request):
        """ Called on each request, before Django decides which view to execute.
 	  Keyword arguments:
          request -- the HttpRequest object
	  Response value:
          None -- An empty value; If it returns None, Django will continue processing this request, executing any other process_request() middleware, then, process_view() middleware, and finally, the appropriate view. 
	  HttpResponse -- An HttpResponse object; If it returns an HttpResponse object, Django won't bother calling any other request, view or exception middleware, or the appropriate view; it will apply response middleware to that HttpResponse, and return the result.
	  NOTE: Request-phase method applied in order, from the top to bottom. This means classes defined at the start of MIDDLEWARE_CLASSES will be run first.
        """
	pass

Listing 4 illustrates the process_view method which is the second overall method to run in a Django middleware class as part of the request phase.

Listing 4 - Django midddleware class process_view method

    def process_view(self, request, view_func, view_args, view_kwargs):
        """ Called on each request, just before Django calls the view.
 	  Keyword arguments:
          request -- the HttpRequest object. 
          view_func -- the Python function that Django is about to use. It's the actual function object, not the name of the function as a string.
	  view_args -- a list of positional arguments that will be passed to the view. Does not include the first view argument (request).
          view_kwargs -- a dictionary of keyword arguments that will be passed to the view. Does not include the first view argument (request).
          Response value:   
          None -- An empty value; If it returns None, Django will continue processing this request, executing any other process_view() middleware and, then, the appropriate view. 
          HttpResponse -- An HttpResponse object; If it returns an HttpResponse object, Django won't bother calling any other view or exception middleware, or the appropriate view; it'll apply response middleware to that HttpResponse, and return the result.
	  NOTE: Request-phase method applied in order, from the top to bottom. This means classes defined at the start of MIDDLEWARE_CLASSES will be run first.
         """
	pass

Listing 5 illustrates the process_exception method which is the first method of the response phase and runs only if an exception occurs in the underlying view.

Listing 5 - Django midddleware class process_exception method

    def process_exception(self, request, exception):
        """ Called when a view raises an exception.
        Keyword arguments:
        request -- the HttpRequest object. 
        exception -- an Exception object raised by the view function.
        Response value: 
        None -- An empty value; the default exception handling kicks in.
	HttpResponse -- An HttpResponse object; If it returns an HttpResponse object, the template response and response middleware will be applied, and the resulting response returned to the browser. If an exception middleware returns a response, the middleware classes above that middleware will not be called at all.
        NOTE: Response-phase method applied in reverse order, from the bottom up. This means classes defined at the end of MIDDLEWARE_CLASSES will be run first.
        """
	pass

Listing 6 illustrates the process_template_response method which is the second method of the response phase and runs only if the response instance has a render() method, indicating that it's a TemplateResponse or equivalent.

Listing 6 - Django midddleware class process_template_response method

    def process_template_response(self, request, response):
        """  Called just after the view has finished executing.
 	 Keyword arguments:
         request -- the HttpRequest object. 
	 response -- the TemplateResponse object (or equivalent) returned by a Django view or by a middleware.
         Response value: 
	 TemplateResponse or equivalent response object that implements a render method. It could alter the given response by changing response.template_name and response.context_data, or it could create and return a brand-new TemplateResponse or equivalent.
          NOTE: You don't need to explicitly render responses, responses are automatically rendered once all template response middleware has been called.
	  NOTE2: Response-phase method applied in reverse order, from the bottom up. This means classes defined at the end of MIDDLEWARE_CLASSES will be run first.
        """
	return response

Listing 7 illustrates the process_response method which is the last overall method to run in a Django middleware class as part of the response phase.

Listing 7 - Django midddleware class process_response method

    def process_response(self, request, response): 
        """ Called on all responses before they're returned to the browser.
 	 Keyword arguments:
         request -- the HttpRequest object. 
         response -- the HttpResponse or StreamingHttpResponse object returned by a Django view or by a middleware.
         Response value: 
         HttpResponse or StreamingHttpResponse -- An HttpResponse or StreamingHttpResponse object; It could alter the given response, or it could create and return a brand-new HttpResponse or StreamingHttpResponse.
         NOTE: Response-phase method applied in reverse order, from the bottom up. This means classes defined at the end of MIDDLEWARE_CLASSES will be run first.
        """
	return response