How to set up custom middleware in Django

First: The path structure

If you don't have it you need to create the middleware folder within your app following the structure:

yourproject/yourapp/middleware

The folder middleware should be placed in the same folder as settings.py, urls, templates...

Important: Don't forget to create the init.py empty file inside the middleware folder so your app recognizes this folder

Second: Create the middleware

Now we should create a file for our custom middleware, in this example let's suppose we want a middleware that filter the users based on their IP, we create a file called filter_ip_middleware.py inside the middleware folder with this code:

class FilterIPMiddleware(object):
    # Check if client IP is allowed
    def process_request(self, request):
        allowed_ips = ['192.168.1.1', '123.123.123.123', etc...] # Authorized ip's
        ip = request.META.get('REMOTE_ADDR') # Get client IP
        if ip not in allowed_ips:
            raise Http403 # If user is not allowed raise Error
 
       # If IP is allowed we don't do anything
       return None

Third: Add the middleware in our 'settings.py'

We need to look for:

  • MIDDLEWARE_CLASSES (django < 1.10)
  • MIDDLEWARE (django >= 1.10)

Inside the settings.py we need to add our middleware (Add it in the last position). It should look like:

MIDDLEWARE = ( #  Before Django 1.10 the setting name was 'MIDDLEWARE_CLASSES'
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
     # Above are django standard middlewares

     # Now we add here our custom middleware
     'yourapp.middleware.filter_ip_middleware.FilterIPMiddleware'
)

Done! Now every request from every client will call your custom middleware and process your custom code!


Writing middleware in Django>=1.10

Since Django 1.10, a middleware class must accept a get_response argument in its __init__() method and provide a __call__() method. Although this can be achieved by using the django.utils.deprecation.MiddlewareMixin when defining a middleware class (as shown in the answer by W.Perrin), creating a class-based middleware in the currently supported versions of Django looks like this:

class CustomMiddleware(object):
    def __init__(self, get_response):
        """
        One-time configuration and initialisation.
        """
        self.get_response = get_response

    def __call__(self, request):
        """
        Code to be executed for each request before the view (and later
        middleware) are called.
        """
        response = self.get_response(request)
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        Called just before Django calls the view.
        """
        return None

    def process_exception(self, request, exception):
        """
        Called when a view raises an exception.
        """
        return None

    def process_template_response(self, request, response):
        """
        Called just after the view has finished executing.
        """
        return response

The process_view(), process_exception() and process_template_response() are special hooks, called by Django when processing the middleware, you may define in your middleware class. In the example above, the implemented hooks will do nothing special expect for making sure that Django will call the next middleware to further process the response/request.

Activating middleware

To activate the middleware component, add it to the MIDDLEWARE list in your Django settings.

MIDDLEWARE = [
    # Default Django 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',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',

    # Add your custom middleware
    'path.to.your.middleware.CustomMiddleware',
]


Just two steps. It works for me with django2.1.

1.Create your own Middleware class.

There is a good demo from official manual.

https://docs.djangoproject.com/en/2.1/ref/request-response/#django.http.HttpRequest.get_host

    from django.utils.deprecation import MiddlewareMixin

    class MultipleProxyMiddleware(MiddlewareMixin):
        FORWARDED_FOR_FIELDS = [
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_FORWARDED_HOST',
            'HTTP_X_FORWARDED_SERVER',
        ]

        def process_request(self, request):
            """
            Rewrites the proxy headers so that only the most
            recent proxy is used.
            """
            for field in self.FORWARDED_FOR_FIELDS:
                if field in request.META:
                    if ',' in request.META[field]:
                        parts = request.META[field].split(',')
                        request.META[field] = parts[-1].strip()

2.Reference your Middleware class in the MIDDLEWARE list of your project setting.py file.

The rule for Middleware reference is the path to your class from the root directory of your project.

For example, in a project named mysite,the tree is as follow.

├── mysite
│   ├── manage.py
│   ├── mysite
│   │   ├── __init__.py
│   │   ├── middleware.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py

We just add our Middleware class MultipleProxyMiddleware in the middleware.py file. We get the following reference name.

MIDDLEWARE = [
    'mysite.middleware.MultipleProxyMiddleware',  
     ...
]