Django Middleware - The new version of middleware and how to use it?

(Comments)

What is Django's Middleware?

Django's middleware framework allows the developers to hook into the request/response processing. They allow us to alter the request/response globally on every request/response cycle. For example the AuthenticationMiddleware alters and associates the request object with a user (using sessions) for each request, which is passed on to the views.

Django's old middleware

Django's middleware has changed quiet a bit in the version 1.10. Prior to version 1.10, there were 5 hooks. The process_request(), process_view(), process_exception(), process_template_response() and process_response(). Any class that implements any of these methods can act as a middleware. All the middleware are specified in the setting MIDDLEWARE_CLASSES which is a list of full class paths (strings).


The first two of the methods, process_request(), process_view() of middleware are executed before the execution of the view ie., that is before the request is built and passed to the view. The next three methods, process_exception(), process_template_response() and process_response() are executed once the view returns the response object and they process the response object.

Django's new middleware

While the old middleware is a class, the new middleware is any callable which takes a get_response callable and returns a callable that takes the request and returns a response. These middlewares are specified in the MIDDLEWARE setting. The skeleton of such a middleware will look like this:

def simple_middleware(get_response):
    # One-time configuration and initialization.

    def middleware(request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

    return middleware

This is function that returns another function which will process the request and response. Similarly, the middleware can be a class that implements the __call__ method, which would look like this:

class SimpleMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

Both the examples above are from the official Django documentation. The get_response can be either the view that returns the response or the next middleware in the list. It is a view only if the middleware is the last one in the list. This forms a call stack of the middleware in the order specified in the settings, hence the request is processed in the specified order of the middleware and the response in the reverse order.


To activate our middleware all we need to do is to add the middleware path in the MIDDLEWARE setting.

An example middleware

This example is inspired from the django oauth toolkit which uses middleware to authenticate requests and integrate it with other Django's authentication methods. Let us take a look how it would look:

def oauth_authentication_middleware(get_response):
    def middleware(request):
        bearer_token = request.META.get("HTTP_AUTHORIZATION", "")
        if bearer_token.startswith("Bearer"):
            user = get_user_from_token(bearer_token)
            if user:
                request.user = request._cached_user = user

        return get_response(request)
    return middleware

Then we just need to add the method's path in the MIDDLEWARE setting.

MIDDLEWARE = [
    '...', # Other middleware
    'my_app.middlware.oauth_authentication_middleware',
    '...',
]

I think the function is self-explanatory. The function takes the get_response callable and returns another function named middleware. The middleware function checks the HTTP_AUTHORIZATION header and reads the bearer token. Based on the bearer token an authorized user is identified. Once a user is identified the model is added as an attribute of the request (we should actually use an authentication backend to identify the user to set other flags like is_authenticated()). Now this user is available in all the requests passed to a view.


This method (middleware) is executed every time a request is made.

Upgrading the old style middleware.

The old middleware are classes as we have already discussed. For the old style middleware to work with the new style also it must extend the django.utils.deprecation.MiddlewareMixin. The MiddlewareMixin will turn the class object into a callable. The mixin class is defined as follows.

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response

It just implements the __call__ method which in turn calls the process_request() and process_response() in the order as required by the new middleware. The following code could be useful to define the middleware that works on both old (prior version 1.10) and the new versions of Django.

try:
    from django.utils.deprecation import MiddlewareMixin
except ImportError:
    MiddlewareMixin = object

Once we decide which class we need to extend (MiddlewareMixin is not available in the older versions of Django), we can build our middleware class as follows:

class MyMiddlewareClass(MiddlewareMixin):
    def process_request(self, request):
        # Process the request
        pass

    def process_response(self, request, response):
        # Process the response
        return response

This is work with both the older and new middleware styles.

Comments

Recent Posts

Archive

2021
2020
2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom