current position:Home>Django: Middleware

Django: Middleware

2022-02-02 14:14:40 Ice sky

Django Middleware

Middleware introduction

Middleware, The middleware , Django In the process of processing received requests and responses , Select and filter the received requests and responses through middleware , So as to obtain the corresponding input and output , In fact Django One of the hook frame , Before calling the corresponding server-side function ,hook The function will receive the request or response first , Deal with it accordingly , This is it. Middleware.

In fact, for a request sent by the user's browser ,Middleware It's actually a request from the client to several layers between views , And in Django Call the view function , When a response is returned , In turn, this response will be returned to the browser through several middleware from the view , Output the corresponding value , This is it. Django Middleware Of request/response Pattern :

client explorer middleware_1 middleware_2 middleware_k views ... request process with request request process with request request process with request request call view function response process with response response process with response response process with response client explorer middleware_1 middleware_2 middleware_k views

This looks like a recursive call procedure , The request at the top of the last stack of the recursive stack finally becomes the of the corresponding view function called request Parameters , And generate the corresponding response. and response Again , As a return value, start from the top of the stack and return layer by layer with recursive return. The final response is sent to the client browser .

Django Middleware can be implemented in two ways , The first is the way of function , This function takes in a get_response The callable function of , Finally, return another callable function middleware, This function takes in a request And return a response object :

def your_middleware(get_response):
    
    def middleware(request):
        
        # executing code for each request before views or later middleware to be called
        
        response = get_response(request)
        
        # executing code for each request/response afater the view is called
        
        return response
    
    return middleware

You can also define a class that can be called by an instance :

class YourMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        return
    
    def __call__(self, request):
        
        # executing code for each request before views or later middleware to be called
        
        response = get_response(request)
        
        # executing code for each request/response afater the view is called
        
        return response

Note: When defining a middleware in the way of class , Initialization function except self There are and can only be get_response This is a parameter , But in __init__ Some global variables can also be defined in . Again ,__init__ Like other classes , Call it once , And for __call__ Come on , It will be called once for each received request .

also , If a middleware is not calling get_response In this case, you want to return a response, Then the subsequent middleware and the final views.py No requests will be received , There will be no response , Instead, the response will be returned directly from the middleware :

client explorer middleware_1 middleware_2 middleware_k views ... request process with request request No request passing process with request without calling get_response response process with response response client explorer middleware_1 middleware_2 middleware_k views

Next , By means of setting.py Add the full path of this middleware to MIDDLEWARE In this list variable , This path should be a complete Python route :

MIDDLEWARE_CLASSES = [
    'yourapp.views.yourmiddleware',
    # default middleware classes below
    # ...
]

In this way, the middleware can be activated .

Other special methods for class-based middleware

The following functions can also be implemented in the defined middleware class :

class simplemiddleware:
    # def __init__ ...
    # def __call__ ...
    
    def process_view(request, view_func, view_args, view_kwargs):
        
        # do something before view function called
        return # response or None
    
    def process_template_response(request, response):
        
        # do something after view function called
        return # response or None
    
    def process_exception(request, exception):
        
        # do something for exception handling
        return # response or None

process_view

process_view(request, view_func, view_args, view_kwargs)

Django The view function will be called , That is, call the function before performing the relevant processing of the view. . This function needs to return a None Or a HttpResponse. If the value returned is None, be Django Will continue to process this request , Perform other process_view middleware , Until the last view . If the current process_view The middleware has returned a HttpResponse object , be Django Other subsequent middleware will not be executed , View of the direct call response , Then call the corresponding response processing middleware for the returned response object , Finally, a result is returned to the client server .

process_exception

process_exception(request, exception)

Django When the view is processed , That is, when an error occurs during the execution of the view function, this function is called . Again , This function needs to return a None Or a HttpResponse object , And returning to HttpResponse When , Call the corresponding response middleware for this response object , Finally, a result is returned to the client browser

process_template_response

process_template_response(request, response)

Django The request (request) It's all one HttpRequest The object of , And respond (response) All are TemplateResponse object , Returned by the view function or after corresponding processing by the middleware .

Django If and only if the view processing is completed , That is, the function is called after the execution of the view function. , If the response object received by this function contains a render() Method words , Then this response is a TemplateResponse. This function must return an implementation render Method , You can change the value in the received response object template_name Field ,context_data Field , Or go straight back to a new TemplateResponse.

Note: There is no need to render the response explicitly , When all middleware calls are completed , The rendering of this response will be done automatically

Streaming response processing

because StreamingHttpResponse It doesn't contain content label , Therefore, when the middleware processes a request content accordingly , You cannot directly assume that every response contains this tag , Therefore, different processing is required for different response types :

if response.streaming:
    #  If it's a streaming response ,
    response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
    #  If not , Call the corresponding response content processing function 
    response.content = alter_content(response.content)

Note: Usually, the response content of streaming response is very large ( For example, large files , Or log files ), Therefore, middleware should wrap the response content through a generator , And don't do any " consumption "( The generator is the producer in the corresponding producer pattern ), When and only when the data is transmitted, the segmented data transmission is carried out through this generator . The wrapper implementation of the generator is as follows :

def wrap_streaming_content(content):
    for chunk in content:
        yield alter_content(chunk)

exception handling

Exceptions thrown by a middleware or view ,Django This exception will be converted between middleware and middleware or between middleware and view , For example, a subsequent middleware throws a page not found 404 It's abnormal , For current middleware , This exception is in the inner layer ( Review the multi-layer structure of middleware ) Hidden in , The current middleware will receive a 404 Error code Http Respond to :

middleware 1 middleware k middleware k+1 ... request raises exception: page not found 404 converts to error code Http response receive Http response with error code without any excpetion middleware 1 middleware k middleware k+1

asynchronous

Django Middleware can support any combination of non asynchronous and asynchronous requests , However, the default middleware can only process synchronization requests . In order to make the middleware have the ability to process asynchronous requests , You need to set two tags in the middleware class or function :

  • sync_capable - boolean: Whether the middleware can handle synchronization requests , The default is True
  • async_capable - boolean: Whether middleware can handle asynchronous requests , The default is False

When the middleware cannot handle both requests at the same time ,Django You need to transform the request to adapt the middleware , And this will have an impact on performance (performance penalty); But when both variables are set to True When ,Django The request will be passed directly to the middleware , No conversion .

stay django.utils.decorators Three decorators are provided in the module to apply the above two tag variables :

  • sync_only_middleware: namely sync_capable = True, async_capable = False
  • async_only_middleware: namely sync_capable = False, async_capable = True
  • sync_and_async_middleware: namely sync_capable = True, async_capable = True

By using sync_and_async_middleware This decorator , It allows our middleware to handle asynchronous and non asynchronous requests at the same time . This is the time , Middleware can be through async.iscoroutinefunction Judge get_response Whether the object is a coroutine function, To distinguish asynchronous requests from non asynchronous requests :

import asyncio
from django.utils.decorators import sync_and_async_middleware

@sync_and_async_middleware
def simple_middle(get_response):
    # One-time configuration and initialization goes here
    if asyncio.iscoroutinefunction(get_response):
        #  Asynchronous processing 
        async def middleware(request):
            # Do something here
            response = await get_response(request)
            return reponse
    else:
        #  Non asynchronous 
        def middleware(request):
            # Do something here
            resposne = get_response(request)
            return response
    return middleware

Note: If the middleware also includes process_view,process_template_response as well as process_exception, You also need to adapt the corresponding decorator to ensure that each function is in the same processing mode

copyright notice
author[Ice sky],Please bring the original link to reprint, thank you.
https://en.pythonmana.com/2022/02/202202021414253626.html

Random recommended