Django 處理http請求之中間件


Django處理http請求之中間件

by:授客 QQ1033553122 歡迎加入全國軟件測試交流QQ群:7156436

 

測試環境

Win7

Django 1.11

 

自定義中間件

中間件“工廠”是一個攜帶一個可調用get_response參數並返回一個中間件的的可調用對象。中間件則是一個攜帶request參數並返回一個response的可調用對象,正如view視圖函數。

中間件可以寫成類似如下的函數(假設以下代碼位於 my_middleware.py文件中,項目結構如下):

 

 

 

 

 

def simple_middleware(get_response):

    print('進入中間件')

 

    def middleware(request):

        # 針對每個request,這里的代碼,會在view、后續中間件被調用之前執行(Code to be executed for each request before the view (and later      middleware) are called.

 

         response = get_response(request)

 

        # 針對每個request,這里的代碼,會在view、后續中間件被調用之后執行(Code to be executed for each request/response after the view is called

 

        return response

 

    return middleware

 

或者如下,寫成一個類,該類的實例為一個可調用對象

class SimpleMiddleware:

    def __init__(self, get_response):

    self.get_response = get_response

        # 只配置並初始化一次(one-time configuration and initialization.

 

    def __call__(self, request):

        # 針對每個request,這里的代碼,會在view、后續中間件被調用之前執行(Code to be executed for each request before the view (and later middleware) are called.

 

        response = self.get_response(request)

 

        # 針對每個request,這里的代碼,會在view、后續中間件被調用之前執行(Code to be executed for each request before the view (and later middleware) are called.

        return response

 

django提供的get_response可能是實際view視圖(如果當前中間是list中配置的最后一個中間件)、下一個中間件,當前中間件不需要知道它是啥。

中間件可以放在python path中的任何地方

 

__init__(get_response)

中間件工廠必須接受一個get_response參數,可以為中間件初始化一些全局狀態,但是要注意:

  • Django只允許用get_response初始化中間件,所以__init__()定義不能包含其它任何參數的。
  • __call__()方法不一樣,針對每個request__call__()都會被調用一次,而__init__()僅在web 服務器啟動時被調用一次(注意:實踐表明 setting.py DEBUG = True時,啟動服務時,__init__()可能被調用兩次)

標記不被使用的中間件

在對應中間件的 __init__() 方法中拋出 MiddlewareNotUsed,Django將會在處理中間件時移除對應的中間件,並在DEBUG設置為True的情況下,往django.request logger中寫入一條調試消息。

 

激活中間件

添加目標中間件到settings.py中的MIDDLEWARE list中以激活中間件,注意新增中間件后需要重啟服務器。

MIDDLEWARE中,每個中間件以一個字符串表示:指向中間件工廠類、函數的全python路徑。如下:

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',
'website.middleware.my_middleware.simple_middleware',
'website.middleware.my_middleware.SimpleMiddleware',
]

 

MIDDLEWARE可以配置為空,但是強烈建議至少包含CommonMiddleware

中間件在MIDDLEWARE中的順序很關鍵,因為一個中間件可能會依賴另一個中間件。例如 AuthenticationMiddleware在會話中存儲已授權用戶信息,所以,它必須在SessionMiddleware之后運行所以,自定義中間件建議都放到最后面。See Middleware ordering for some common hints about ordering of Django middleware classes。

 

中間件順序和分層

request階段,view調用之前,Django會按順序-中間件在MIDDLEWARE中的定義,從上往下(索引從小到大),把中間件作用於request(During the request phase, before calling the view, Django applies middleware in the order it’s defined in MIDDLEWARE, top-down)

可以把它看成一個洋蔥:每個中間件類都是一層包裹了view視圖(洋蔥的核心)的皮,如果請求通過了洋蔥所有皮(每層都會調用get_response以便把request傳遞給下一層),到達核心view,那么將按相反的順序,把response一層一層的往外傳。

如果其中一層短路了,沒有調用get_response的情況下,返回了response,該層所包裹的所有層(包括view視圖)將看不到當前requestresponseresponse只會經過request已經通過的層。

 

其它中間件鈎子

除了上述描述的基礎的request/response中間件模式,還可以添加以下三種特定的方法給基於類的中間件:

process_view()

process_view(requestview_funcview_argsview_kwargs)

request 為一個 HttpRequest 對象。

view_func為Django即將調用的python函數 (實際函數對象,而非表示函數名稱的字符串

view_args 傳遞給view函數的位置參數list列表

view_kwargs 傳遞給view函數的字典參數,不管是view_args 還是 view_kwargs都不包含第一個參數(request).

process_view() Django調用view之前,__call__()被調用之后被調用,如下:

__call__() ->process_view() -> view function -> __call__()

函數應該返回None或者一個HttpResponse對象。如果返回NoneDjango將繼續處理request,執行其它中間件的process_view(),最后執行對應的view。如果返回一個HttpResponse對象,Django將不會調用對應的view及后續的process_exception(), process_template_response()等,直接調用對應的response中間件作用於該response對象並返回結果.

注意:

應該避免在view視圖運行之前,在中間件內部訪問 request.POST因為這將阻止該中間件之后的任何視圖 modify the upload handlers for the request(Accessing request.POST inside middleware before the view runs or in process_view() will prevent any view running after the middleware from being able to modify the upload handlers for the request, and should normally be avoided)

CsrfViewMiddleware類可以被看做一個異常,因為它提供csrf_exempt() 和csrf_protect() 裝飾器,可以顯示控制在哪里進行CSRF校驗。 (The CsrfViewMiddleware class can be considered an exception, as it provides the csrf_exempt() andcsrf_protect() decorators which allow views to explicitly control at what point the CSRF validation should occur

 

process_exception()

process_exception(requestexception)

request 為一個 HttpRequest 對象。

exception 為view視圖函數的一個 Exception 對象。

view拋出一個異常時,Django才會調用process_exception()。函數應該返回None或者一個HttpResponse對象。如果返回一個HttpResponse對象,將應用template responseresponse中間件並返回上述描述的HttpResponse對象,結果給瀏覽器,否則走默認的異常處理(default exception handling 

相反的,response階段(包括process_exception),按逆序運行中間件。如果異常中間件返回了一個response,位於該中間件前面的中間件(MIDDLEWARElist 中對應索引比當前中間件的索引小的中間件)的process_exception都不會被調用。

 

process_template_response()

process_template_response(requestresponse)

request  為一個 HttpRequest 對象。

response 為Django view、中間件返回的一個TemplateResponse對象

process_template_response() view視圖執行完成后才被調用。如果response實例有render()方法,它將被視為TemplateResponse 。形如:

from django.template.response import TemplateResponse

 

def test_page(request):

return TemplateResponse(request, 'website/pages/mytest.html',{})

 

它必須返回實現了render方法的response對象。可以通過改變response.template_nameresponse.context_data更改給定response,或者返回一個全新的TemplateResponse

無需顯示的渲染response--response將在所有template response中間件調用完成后自動被渲染。

response階段(包括process_template_response()),按逆序運行中間件。

 

Dealing with streaming responses

不同於HttpResponse,StreamingHttpResponse沒有content屬性,因此中間件不能認為所有的響應都有content 屬性,如果想要訪問content,需要測試流式響應:

if response.streaming:

response.streaming_content = wrap_streaming_content(response.streaming_content)

else:

response.content = alter_content(response.content)

 

注意:streaming_content被假定為太大而不能存放在內存中,響應中間件可以將它包裹在一個生成器中,但是不能消費它,通常如下所示:

Def wrap_streaming_content(content):

for chunk in content:

yield alter_content(chunk)

 

Exception handling

例子1

# -*- coding: utf-8 -*-
 
__author__ = 'shouke'
 
# from django.http import HttpResponse
 
class SimpleMiddleware1:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.
        print('call __init__ in SimpleMiddleware1')
 
    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        print('call __call_ in SimpleMiddleware1 before the view is called')
 
        response = self.get_response(request)
 
       # Code to be executed for each request/response after
        # the view is called.
        print('call __call_ in SimpleMiddleware1 after the view is called')
        return response
 
 
    def process_view(self, request, view_func, view_args, view_kwargs):
        print('call process_view in SimpleMiddleware1')
        # return HttpResponse('shouke')
 
 
    def process_template_response(self, request, response):
        print('call process_template_response in SimpleMiddleware1')
        return response
 
 
    def process_exception(self, request, exception):
        print('call process_exception in SimpleMiddleware1')
 
 
class SimpleMiddleware2:
    def __init__(self, get_response):
       self.get_response = get_response
       # One-time configuration and initialization.
       print('call __init__ in SimpleMiddleware2')
 
    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        print('call __call_ in SimpleMiddleware2 before the view is called')
 
        response = self.get_response(request)
 
        # Code to be executed for each request/response after
        # the view is called.
        print('call __call_ in SimpleMiddleware2 after the view is called')
        return response
 
 
    def process_view(self, request, view_func, view_args, view_kwargs):
        print('call process_view in SimpleMiddleware2')
 
 
    def process_template_response(self, request, response):
        print('call process_template_response in SimpleMiddleware2')
        return response
 
 
    def process_exception(self, request, exception):
        print('call process_exception in SimpleMiddleware2')
        # return HttpResponse('shouke')

 

View函數

def test_page(request):

    print('call view function test_page')

    # 1/0

 

    return TemplateResponse(request, 'website/pages/mytest.html',{})

 

中間件配置

MIDDLEWARE = [

     ……

    'website.middleware.my_middleware.SimpleMiddleware1',

    'website.middleware.my_middleware.SimpleMiddleware2',]

 

運行結果

 

 

 

 

Upgrading pre-Django 1.10-style middleware

 
From django.utils.deprecation import MiddlewareMixin

 

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

 

Django提供了django.utils.deprecation.MiddlewareMixin來簡化中間件類的創建,MiddlewareMixin兼容 MIDDLEWARE 和老版本的 MIDDLEWARE_CLASSES。Django包含的所有中間件類都是兼容彼此的配置的。

如果使用 MIDDLEWARE_CLASSES, 將不會調用__call__;直接調用 process_request() 和process_response() 

大多數情況下,直接從MiddlewareMixin繼承創建中間件就夠了。

使用 MIDDLEWARE 和MIDDLEWARE_CLASSES的區別?

 

例子2

修改中間件代碼如下,其它保持不變
# -*- coding: utf-8 -*-
 
__author__ = 'shouke'
 
from django.utils.deprecation import MiddlewareMixin
# from django.http import HttpResponse
 
class SimpleMiddleware1(MiddlewareMixin):
    def process_request(self, request):
        print('call process_request in SimpleMiddleware1')
 
 
    def process_response(self, request, response):
        print('call process_response in SimpleMiddleware1')
        return response
 
 
 
class SimpleMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('call process_request in SimpleMiddleware2')
 
    def process_response(self, request, response):
        print('call process_response in SimpleMiddleware2')
        return response
 
運行結果

 

 

 
 
        

 

說明:

process_request在調用view函數視圖之前執行;

Process_response在調用view函數視圖之后執行;

 

參考鏈接

https://docs.djangoproject.com/en/2.1/topics/http/middleware/

https://docs.djangoproject.com/en/2.1/_modules/django/middleware/common/#CommonMiddleware

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM