我們在使用python的一些庫時,會遇到中間件這個概念,比如scrapy和Django,那麼什麼是中間件呢?
什麼是中間件
中間件就是在目標和結果之間進行的額外處理過程,在Django中就是request和response之間進行的處理,相對來說實現起來比較簡單,但是要注意它是對全局有效的,可以在全局範圍內改變輸入和輸出結果,因此需要謹慎使用,否則不僅會造成難以定位的錯誤,而且可能會影響整體性能。
中間件有什麼用
如果想要修改HttpRequest或者HttpResponse,就可以通過中間件來實現。
- 登陸認證:在中間件中加入登陸認證,所有請求就自動擁有登陸認證,如果需要放開部分路由,只需要特殊處理就可以了。
- 流量統計:可以針對一些渲染頁面統計訪問流量。
- 惡意請求攔截:統計IP請求次數,可以進行頻次限制或者封禁IP。
中間件執行流程
在Django中自定義中間件是非常簡單的,在settings.py中有一個配置項:
<code>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',
]/<code>
只要把添加的中間件配置在這裡就可以了。每一箇中間件都是一個類,多箇中間件可以寫在同一個文件,也可以在獨立文件中。每個中間件可以包含五個方法:
<code>process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)/<code>
我在網上找到這麼一張圖片,說明了請求的數據流在Django中間件當中的執行流程
中間件函數執行流程
- 請求到達中間件後先依次執行每個中間件的process_request函數
- 然後再依次執行每個中間件的process_view函數,找到我們的視圖函數
- 執行視圖函數處理請求數據
- 如果在上面的過程中出現異常,則依次反方向執行每個中間件的process_exception函數
- 如果請求包含模板渲染,則依次反方向執行每個中間件的process_template_response函數
- 最後依次反方向執行每個中間件的process_response函數
以上這些執行函數將返回None或者HttpResponse對象,如果返回None,則交給下一個中間件的對應函數處理;如果返回HttpResponse對象,則將其返回給用戶
在這些中間件的執行函數中,我們最常用的就是process_request和process_response函數,通常用來在視圖函數處理前和視圖函數處理後執行一些相應的操作,這個要根據我們的業務需求,選擇不同的處理過程。例如:進行登陸認證,因為必須要在視圖函數處理前進行認證,我們可以在process_request中處理;攜帶認證cookies信息,就可以在process_response函數中給response對象增加指定cookies值。
中間件回調函數執行
- Request函數:process_request(self, request)
執行時機:當接收到前端請求,並生成request對象,但是仍未解析url,未確定當前要運行的視圖函數。
如果返回None,Django將繼續處理下一個中間件的request函數;如果返回HttpResponse對象,Django將不再執行其他除process_response以外的所有函數,包括後面的process_request函數、其他中間件函數以及視圖函數。 - View函數:process_view(self, request, callback, callback_args, callback_kwargs)
執行時機:在執行完所有中間件的process_request函數,並且已經匹配到要執行的視圖函數,但是還沒有調用視圖函數之前。
callback:時機要執行的視圖函數對象(就是我們所寫的視圖處理函數)
callback_args:視圖函數的位置參數列表(不包含self和request)
callback_kwargs:視圖函數的關鍵字參數
如果返回None,Django將繼續處理下一個中間件的request函數;如果返回HttpResponse對象,Django將不再執行其他除process_response以外的所有函數,包括 後面的process_request函數、其他中間件函數以及視圖函數。 - Template函數:process_template_response()
執行時機:只有在視圖函數的返回對象中有render方法才會執行,並把render方法的返回值返回給用戶。 - Exception函數:process_exception(self, request, exception)
執行時機:如果在執行過程中出現問題,並且拋出一個未被捕獲的異常時才被調用。我們可以用它來捕獲請求錯誤,發送通知或者恢復錯誤場景。
如果返回None,Django將使用框架內置異常處理,並繼續交給下一個exception函數;如果返回HttpResponse對象,Django將不再執行其他除process_response以外的所有函數,並中斷異常處理。 - Response函數:process_response(self, request, response)
執行時機:執行完view函數並生成response之後,幾乎是必執行的函數。
返回並且只能、必須返回HttpResponse對象,否則會導致HTTP請求中斷。
自定義中間件
- 創建中間件類
<code>from django.utils.deprecation import MiddlewareMixin
class MyCustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('MyCustomMiddleware1')
def process_response(self, request, response):
print('返回 MyCustomMiddleware1')
return response
class MyCustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('MyCustomMiddleware2')
def process_response(self, request, response):
print('返回 MyCustomMiddleware2')
return response/<code>
- 註冊中間件
<code>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',
'MyMiddleware.MyCustomMiddleware1',
'MyMiddleware.MyCustomMiddleware2'
]/<code>
輸出結果:
<code>MyCustomMiddleware1
MyCustomMiddleware2
返回 MyCustomMiddleware2
返回 MyCustomMiddleware1/<code>
系統中間件的用途
- django.middleware.security.SecurityMiddleware主要是針對安全訪問處理,就是把http請求重定向到https請求
- django.contrib.sessions.middleware.SessionMiddleware在Django中我們用的request.session就是在process_request中進行處理的,根據我們在settings中配置的SESSION_COOKIE_NAME變量,從cookies中獲取對應的值,從表中查詢出session值,創建session對象,賦值給request_session對象。
<code>def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)/<code>
在process_response函數中,給response對象設置SESSION_COOKIE_NAME值和過期時間等。
<code>response.set_cookie(
settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
samesite=settings.SESSION_COOKIE_SAMESITE
)/<code>
- django.middleware.common.CommonMiddleware
檢測是否允許瀏覽器類型
<code>if 'HTTP_USER_AGENT' in request.META:
for user_agent_regex in settings.DISALLOWED_USER_AGENTS:
if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
raise PermissionDenied('Forbidden user agent')/<code>
檢查是否需要添加/,主要是根據settings中APPEND_SLASH配置
<code>if self.should_redirect_with_slash(request):
path = self.get_full_path_with_slash(request)
else:
path = request.get_full_path()/<code>
在process_response函數中,會判斷是否需要把404的請求重新定向到我們需要的頁面
- django.middleware.csrf.CsrfViewMiddleware
這個很明顯就是我們Django框架的csrf驗證了,主要是process_view中的處理,從函數處理我們可以看到以下幾點:
- request請求中包含csrf_processing_done屬性,則不進行csrf驗證
- 視圖函數中包含csrf_exempt屬性,則不進行csrf驗證
- 如果是GET、HEAD、OPTIONS、TRACE請求,則不進行csrf驗證
- request請求中包含_dont_enforce_csrf_checks屬性,則不進行csrf驗證
- https請求頭中如果不包含HTTP_REFERER,則拒絕訪問
- 請求頭中不包含CSRF_COOKIE,則拒絕訪問
- POST請求中攜帶csrfmiddlewaretoken參數,如果驗證通過就可以訪問
- PUT/DELETE請求頭中攜帶CSRF_HEADER_NAME配置,如果驗證通過就可以訪問
- django.contrib.auth.middleware.AuthenticationMiddleware這個中間件中為我們的request對象添加了user屬性,主要是獲取session中SESSION_KEY值(settings配置中),從用戶表中查詢對應主鍵,得到用戶對象,將其付給request.user
- django.contrib.messages.middleware.MessageMiddlewareDjango的消息框架,主要是向目標中推送消息內容,在前端可通過以下方式使用
<code>{% if messages %}
{% if mesage.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %}
{{ message }}
{% for message in messages %}
{% endfor %}
{% endif %}/<code>
閱讀更多 Python集結號 的文章