What is middleware?

Middleware is a framework of hooks which works as a ‘plugin’ between Django’s request/response processing. In other words a middleware is a callable that takes a request and returns a response just like a view. Each middleware component is responsible for doing some specific task.

In simple word middlewares are Mediators between client & view.

Let me clear the concept with an example:

Suppose you want to send a letter to your best friend through a post office. You completed the letter & went to post office, but post master told you that you didn’t follow the perfect rules to send a letter. Then you corrected it. After that the postmaster received it & sent it to the post office nearer to your friends home. A postman will take the letter from there & deliver it to your friends home. Here, a problem can be happened too. If you make mistake in writing the address then it won’t be possible to deliver. But if everything is okey the letter will be delivered on time. Then by following the same procedure your friend can send you a response.

In this scenario, you are the client, your friend is the view & the postmaster & postman are the middlewares. As the postmaster & postman can give you feedback similarly middlewares can also give response to the client.

Two types of MIDDLEWARES:

  • Built In Middlewares
  • Custom Middlewares

Built In Middlewares

You can see a MIDDLEWARE list in your settings.py file. These all are built in middlewares.

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, #Custom Middlewares ]

How does middleware work in Django?

Middlewares are executed from top to bottom during request and from bottom to top during response.

Request

User –> Middleware 1 –> Middleware 2 –> Middleware N –> View

Response

User <– Middleware 1 <– Middleware 2 <– Middleware N <– View

Graphical Representation of working procedure of middlewares.

source: https://www.youtube.com/watch?v=tO6HpHtamlw

Custom Middleware Implementation

Middlwares can be implemented using python function or class. So, they are classified in two types on the basis of implementation…

  1. Function-Based Middleware
  2. Class-Based Middleware.

We will only talk about Class Based Middleware here. First see a simple Class Based Middleware. This code snippet is taken from django documentation.

class MyMiddleware:

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

   def __call__(self, request):
        #code to executed for each request before the view (and later middleware) are called.
        response = self.get_response(request)
        #code to executed for each request/response       after the view is called.
        return response

__init__() method is the initializer of the middleware. It executes for one time when the server starts running. Django initializes the middleware with only the get_response argument, so you can’t define  __init__()  method requiring any other arguments.

__call__() method is called with each request. The code before response is executed before the view is called and the code after response is executed after view returns response.

The __call__() method:

  1. Calls self.process_request(request) (if defined).
  2. It calls self.get_response(request) to get the response from later middleware and the view.
  3. Calls self.process_response(request, response) (if defined).
  4. Returns the response.

Middleware class has the following hooks:

The process_view(request, view_func, view_args, view_kwargs)

process_exception(request, exception) (only if the view raised an exception)

and process_template_response(request, response) (only for template responses)

Custom Middleware

Normally middlewares work globally. That means each middleware listed in MIDDLEWARE list will be applied in each view. We will work only for one view here. Let’s assume we have a view which returns all the objects of ‘Person’ model.

views.py

class AllPersonsView(ListView):
    model = Person
    template_name = 'persons.html'
    context_object_name = 'persons'

    def __init__(self):
        print("I am View")

This is the url that redirects to our AllPersonsView.

path('all-persons/',AllPersonsView.as_view(),name='all-persons'),

Now, there are some common patterns of searching this view. Like one may not remember the exact ‘all-persons/’ pattern. People can try a bit different patterns like [‘/all-person/’,’/allpersons/’,’/allperson/’, ‘/all-person’,’/allpersons’,’/allperson’]

For each of these case django will show a ‘Page Not Found’ error. Which can be considered as a drawback. We know that all these patterns are similar to ‘all-persons’ path & everyone was supposed to go to ‘all-persons’ page. So, we will make a middleware that will take the path & if the path is similar to any one of these patterns it will redirect to ‘all-persons’ page.

middlewares.py

class PathMiddleware():

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self,request):
        path = request.path
        print('Before View Function')

        possible_searched_paths = ['/all-person/', '/allpersons/','/allperson/','/all-person',                                 '/allpersons', '/allperson']

        if path in possible_searched_paths:
            return redirect('all-persons')

        response = self.get_response(request)
        print('This Is After View')
        return response

Lets add our custom middeware in settings.py MIDDLEWARE list. This will activate our middleware.

#Custom Middleware  
'first_app.middlewares.PathMiddleware',

Let’s run our server and try to visit these urls below.

http://127.0.0.1:8000/all-person/
http://127.0.0.1:8000/allpersons/
http://127.0.0.1:8000/allperson/
http://127.0.0.1:8000/all-person
http://127.0.0.1:8000/allpersons
http://127.0.0.1:8000/allperson

All of these will redirect to ‘127.0.0.1:8000/all-persons‘ url.

in shell:

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
April 14, 2021 - 15:52:02
Django version 3.1.7, using settings 'my_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
Before View Function
I am View
This Is After View

This was a simple overview of Custom Middlewares. For more see django documentation.

+ posts

Author | Python-Django Developer

+ posts

Full-stack Developer (Python | Django | React | React-Native | Angular | Vue)