Five Usages and Logical Processes of Developing Advanced-Django Framework Middleware in Python 3

Keywords: Python Django Session less Database

Reading catalogue

  1. What is Middleware
  2. Execution flow of Middleware
  3. Logical Process of Middleware

1. What is middleware?

Officially, middleware is a framework-level hook for handling Django requests and responses. It is a lightweight, low-level plug-in system for changing Django input and output globally.

Each middleware component is responsible for doing some specific functions. However, due to its global impact, it needs to be used cautiously, and improper use will affect performance.

To put it bluntly, middleware helps us to do some additional operations before and after the execution of view functions. It is essentially a custom class, in which several methods are defined.

The Django framework executes these methods at a specific time of request.

We've been using middleware all the time, but we haven't noticed it. Open the Settings.py file of the Django project and see the MIDDLEWARE configuration item in the figure below.

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',
]

MIDDLEWARE configuration items are a list of strings, which are actually classes, that is, middleware.

Have we been exposed to a csrf-related middleware before? We start by asking people to comment him out and then submit a post request, and then we won't be forbidden.

After learning to use csrf_token, I stopped annotating this middleware. The next step is to learn the methods in middleware and when they will be implemented.

2. Custom Middleware

Middleware can define five methods: process_request and process_response.

  • process_request(self,request)
  • process_view(self, request, view_func, view_args, view_kwargs)
  • process_template_response(self,request,response)
  • process_exception(self, request, exception)
  • process_response(self, request, response)

The return value of the above method can be None or an HttpResponse object. If it is None, it will continue to execute backwards according to the rules defined by django. If it is an HttpResponse object, it will be returned directly to the user.

1. Customize a middleware example

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1 Inside process_request")

    def process_response(self, request, response):
        print("MD1 Inside process_response")
        return response

2,process_request

The process_request has one parameter, request, which is the same as the request in the view function.

Its return value can be either None or HttpResponse object. If the return value is None, follow the normal process and leave it to the next middleware, if it is an HttpResponse object.

Django will not execute the view function, but return the corresponding object to the browser. Let's look at how Django performs the process_request method in multiple middleware.

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1 Inside process_request")

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2 Inside process_request")
        pass

Register the above two custom MIDDLEWARE in the MIDLEWARE configuration item of 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',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'middlewares.MD1',  # Custom middleware MD1
    'middlewares.MD2'  # Custom middleware MD2
]

At this point, when we visit a view, we will find that the following contents are printed in the terminal:

process_request in MD1
process_request in MD2
index view in app01

If you change the position of MD1 and MD2 and visit another view, you will find that the contents printed in the terminal are as follows:

process_request in MD2
process_request in MD1
index view in app01

Looking at the results, we know that the view function is still the last one to execute, and MD2 executes its own process_request method before MD1.

When you print the request parameters in the process_request method of two custom middleware, you will find that they are the same object.

In conclusion:

  1. The process_request method of the middleware is executed before the view function is executed.
  2. When configuring multiple middleware, it is executed from front to back in the order of registration in MIDDLEWARE, that is, the index value of the list.
  3. Requests passed between different middleware are the same object
  4. Return value 1. Return None, and do any good to proceed directly to the next step
  5. Return value 2. Return the response object and return the response directly (process_request, no urls.py and views.py of the subsequent middleware)

The process_response method in multiple middleware is executed in reverse order according to the registration order in MIDDLEWARE, that is to say, the process_request method of the first middleware is executed first.

Its process_response method is executed last, the process_request method of the last middleware is executed last, and its process_response method is executed first.

3,process_response

It has two parameters, one is request, the other is response, request is the same object in the above example, response is the HttpResponse object returned by the view function.

The return value of this method must also be an HttpResponse object. Add process_response method to M1 and M2 above:

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1 Inside process_request")

    def process_response(self, request, response):
        print("MD1 Inside process_response")
        return response

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2 Inside process_request")
        pass

    def process_response(self, request, response):
        print("MD2 Inside process_response")
        return response

Visit a view to see the output of the terminal:

process_request in MD2
process_request in MD1
index view in app01
process_response in MD1
process_response in MD2

Looking at the results, we can see that:

The process_response method is executed after the view function in the order that MD1 executes first than MD2. (MD2 in settings.py is registered before MD1)

The process_response method in multiple middleware is executed in reverse order according to the registration order in MIDDLEWARE, that is to say, the process_request method of the first middleware is executed first.

Its process_response method is executed last, the process_request method of the last middleware is executed last, and its process_response method is executed first.

Conclusion:

  1. Execute after views.py returns the response object
  2. The order of execution is in reverse order of registration in the list.
  3. The return value must have a return value, which is the response object.

4,process_view

process_view(self, request, view_func, view_args, view_kwargs)

The method has four parameters

request is the HttpRequest object.

view_func is the view function that Django will use soon. (It's the actual function object, not the name of the function as a string.)

view_args is a list of location parameters passed to the view.

View_kwargs is a dictionary of keyword parameters passed to the view. view_args and view_kwargs do not contain the first view parameter (request).

Django calls the process_view method before calling the view function.

It should return None or an HttpResponse object. If None is returned, Django will continue to process the request, execute the process_view method of any other middleware, and then execute the corresponding view.

If it returns an HttpResponse object, Django will not call the appropriate view function. It will execute the middleware process_response method and apply it to the HttpResponse and return the result.

Add process_view method to MD1 and MD2:

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1 Inside process_request")

    def process_response(self, request, response):
        print("MD1 Inside process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 Medium process_view")
        print(view_func, view_func.__name__)

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2 Inside process_request")
        pass

    def process_response(self, request, response):
        print("MD2 Inside process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 Medium process_view")
        print(view_func, view_func.__name__)

Visit the index view function to see the output:

process_request in MD2
process_request in MD1
--------------------------------------------------------------------------------
process_view in MD2
<function index at 0x000001DE68317488> index
--------------------------------------------------------------------------------
process_view in MD1
<function index at 0x000001DE68317488> index
index view in app01
process_response in MD1
process_response in MD2

The process_view method is executed after the process_request and before the view function. The execution order is in the order of registration in MIDDLEWARE from front to back.

Conclusion:

  1. Before executing the real view function after urls.py
  2. Execute in the order registered in the list
  3. Return value 1. Return None, square line
  4. Return value 2. Return the response object, jump out directly, and execute the process_response method of all Middleware in reverse order.

5,process_exception

process_exception(self, request, exception)

This method has two parameters: one is HttpRequest object, the other is Exception object generated by view function exception.

This method is executed only if an exception occurs in the view function, and the value it returns can be either a None or a HttpResponse object. If it is an HttpResponse object,

Django calls the process_response method in the template and middleware and returns it to the browser, otherwise the exception will be handled by default. If a None is returned,

The process_exception method is handed over to the next middleware to handle exceptions. Its execution order is also in reverse order according to the order of middleware registration. Add this method to MD1 and MD2:

from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1 Inside process_request")

    def process_response(self, request, response):
        print("MD1 Inside process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 Medium process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 Medium process_exception")

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2 Inside process_request")
        pass

    def process_response(self, request, response):
        print("MD2 Inside process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 Medium process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 Medium process_exception")

If there are no exceptions in the view function, the process_exception method does not execute.

Find a way to throw an exception in the view function:

def index(request):
    print("app01 Medium index view")
    raise ValueError("Ha-ha")
    return HttpResponse("O98K")

In process_exception of MD1, a response object is returned:

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1 Inside process_request")

    def process_response(self, request, response):
        print("MD1 Inside process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 Medium process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 Medium process_exception")
        return HttpResponse(str(exception))  # Returns a response object

Look at the output:

process_request in MD2
process_request in MD1
--------------------------------------------------------------------------------
process_view in MD2
<function index at 0x0000022C09727488> index
--------------------------------------------------------------------------------
process_view in MD1
<function index at 0x0000022C09727488> index
index view in app01
Ha-ha
process_exception in MD1
process_response in MD1
process_response in MD2

Note that the process_exception method of MD2 is not executed here, because the process_exception method of MD1 directly returns a response object.

6. process_template_response (less used)

process_template_response(self, request, response)

Its parameters, an HttpRequest object, and response are TemplateResponse objects (generated by view functions or middleware).

process_template_response is executed immediately after the execution of the view function, but it has a prerequisite that the object returned by the view function has a render() method.

(Or indicate that the object is a TemplateResponse object or equivalent method).

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1 Inside process_request")

    def process_response(self, request, response):
        print("MD1 Inside process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 Medium process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 Medium process_exception")
        return HttpResponse(str(exception))

    def process_template_response(self, request, response):
        print("MD1 Medium process_template_response")
        return response


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2 Inside process_request")
        pass

    def process_response(self, request, response):
        print("MD2 Inside process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 Medium process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 Medium process_exception")

    def process_template_response(self, request, response):
        print("MD2 Medium process_template_response")
        return response

views.py:

def index(request):
    print("app01 Medium index view")

    def render():
        print("in index/render")
        return HttpResponse("O98K")
    rep = HttpResponse("OK")
    rep.render = render
    return rep

Access the index view and output the result of the terminal:

MD2 Inside process_request
MD1 Inside process_request
--------------------------------------------------------------------------------
MD2 Medium process_view
<function index at 0x000001C111B97488> index
--------------------------------------------------------------------------------
MD1 Medium process_view
<function index at 0x000001C111B97488> index
app01 Medium index view
MD1 Medium process_template_response
MD2 Medium process_template_response
in index/render
MD1 Inside process_response
MD2 Inside process_response

The results show that:

After the view function is executed, the process_template_response method of the middleware is executed immediately, in reverse order, MD1 is executed first, and MD2 is executed.

Then the render method of the HttpResponse object returned by the view function is executed, a new HttpResponse object is returned, and the process_response method of the middleware is executed.

2. The execution process of Middleware

In the previous section, we learned about five methods in middleware, their parameters, return values, and when to execute. Now we summarize the execution process of middleware.

After the request arrives at the middleware, the process_reques method of each registered middleware is executed in positive order. The value returned by the process_request method is None, which is executed sequentially.

If the returned value is the HttpResponse object, instead of executing the subsequent process_request method, the process_response method of the current corresponding middleware is executed.

Return the HttpResponse object to the browser. That is, if six middleware are registered in MIDDLEWARE, the third middleware returns an HttpResponse object during execution.

Then the process_request and process_response methods of the middleware in 4,5,6 are not executed, and the process_response methods of the middleware in 3,2,1 are executed sequentially.

 

After the process_request method is executed, the routing is matched and the view function to be executed is found. First, the process_view method in the middleware is executed without executing the view function.

The process_view method returns None and continues to execute in sequence. After all the process_view methods are executed, the view function is executed. The process_view method added to middleware 3 returns the HttpResponse object.

The process_view and view functions of 4,5,6 are not executed, and they are executed in reverse order from the last middleware, the process_response method of middleware 6.

The triggering of process_template_response and process_exception methods is conditional and the execution order is reverse. Summarize all the implementation processes as follows:

 

 

process_exception,process_template_response needs to meet certain conditions before it can be executed

3. Middleware version login verification

The middleware version of login validation relies on session, so there is a django_session table in the database.

urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^index/$', views.index),
    url(r'^login/$', views.login, name='login'),
]
urls.py

views.py

from django.shortcuts import render, HttpResponse, redirect


def index(request):
    return HttpResponse('this is index')


def home(request):
    return HttpResponse('this is home')


def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user == "duoduo" and pwd == "123456":
            # Set up session
            request.session["user"] = user
            # Get the one before jumping to the landing page URL
            next_url = request.GET.get("next")
            # If so, jump back to the one before landing. URL
            if next_url:
                return redirect(next_url)
            # Otherwise, default jump to index page
            else:
                return redirect("/index/")
    return render(request, "login.html")
views.py

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Login page</title>
</head>
<body>
<form action="{% url 'login' %}">
    <p>
        <label for="user">User name:</label>
        <input type="text" name="user" id="user">
    </p>
    <p>
        <label for="pwd">Password:</label>
        <input type="text" name="pwd" id="pwd">
    </p>
    <input type="submit" value="Sign in">
</form>
</body>
</html>
login.html

middlewares.py

class AuthMD(MiddlewareMixin):
    white_list = ['/login/', ]  # White list
    balck_list = ['/black/', ]  # Blacklist

    def process_request(self, request):
        from django.shortcuts import redirect, HttpResponse

        next_url = request.path_info
        print(request.path_info, request.get_full_path())

        if next_url in self.white_list or request.session.get("user"):
            return
        elif next_url in self.balck_list:
            return HttpResponse('This is an illegal URL')
        else:
            return redirect("/login/?next={}".format(next_url))
Login Check Middleware

Register in 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',
    'middlewares.AuthMD',
]
Registration Middleware in settings.py

After AuthMD middleware registration, all requests follow AuthMD's process_request method.

If the visited URL has user name in the whitelist or session, the normal process will not be blocked.

If the URL is on the blacklist, the string of This is an illegal URL is returned.

Normal URL s but need to be accessed after login, so that the browser jumps to the login page.

Note: AuthMD middleware requires session, so AuthMD registration should be located below the session center.

Attachment: Django request flow chart

 

Posted by ubaldc36 on Fri, 17 May 2019 05:33:07 -0700