最近面試的時候,被面試官問道一個問題,就是 request.user 裏面的 user 是怎樣獲得的,這個問題當時沒有回答上來,能夠說是很是的尷尬,因此趕快查了一些資料,看了一些源碼,特意來總結一下這個問題。前端
要想回答爲何能夠直接經過 request.user 獲得請求的用戶,應該先來看看請求被處理以及如何返回響應的流程。今天先總結一下 django 從請求到響應都進行了哪些過程。python
當客戶端發送一次請求後,最早處理請求的其實是 web 服務器就是咱們常常說的 nginx、Apache 這類的 web 服務器,而 WSGI 的做用就是把 web 服務器和 web 框架 (Django) 鏈接起來。WSGI 被分爲了兩個部分:服務端和應用端。爲了處理一個 WSGI 的響應,服務端執行應用程序並嚮應用端提供一個回調函數,應用端處理請求並使用提供的回調將響應返回給服務端。本質上來說,我以爲 WSGI 就是 web 服務器和 django 應用之間的一個聯繫人。nginx
當用戶向你的應用發送一個請求的時候,一個 WSGI handler 將會被初始化,它會完成如下工做:web
中間件被用在了 django 的許多關鍵功能中:例如,使用 CSRF 中間鍵來防止跨站請求僞造攻擊。它們也被用來處理會話數據,身份認證和受權一樣是由中間件來完成的。咱們也能夠本身編寫中間件來調整或者(短路)經過應用程序的數據流。面試
django 的中間件至少含有如下四個方法中的一個:process_request, process_response, process_view, process_exception。這些方法會被 WSGI handler 收集並按照順序調用。django
咱們能夠先來看看 django.contrib.auth.middleware.AuthenticationMiddleware:緩存
def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request)
return request._cached_user
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request))
複製代碼
這裏咱們能夠發現 request.user 這個屬性是在 AuthenticationMiddleware 中產生的。這個咱們稍後再說。服務器
這裏咱們能夠發現,這個中間件只有 process_request,說明它只在 request 這一步處理流入和流出 django 應用的數據流。這個中間件會首先驗證會話中間件是否被使用,而後經過調用 get_user 函數來設置用戶。當 WSGI 處理程序迭代 process_request 方法列表的時候,它將會構建這個最終會被傳遞給視圖函數的請求對象,並可以使你引用 request.user。一些中間件沒有 process_request 方法,在這個階段,會被跳過。微信
process_request 應該返回 None 或者 HTTPResponse 對象。當返回 None 時,WSGI handler 會繼續加載 process_request 裏面的方法,可是後一種狀況會短路處理過程並進入 process_response 循環。cookie
當全部的 process_request 被調用完以後,咱們就會獲得一個將被傳遞給視圖函數的 request 對象。當這個事件發生以前,django 必須解析 url 並決定調用哪個視圖函數。這個過程很是簡單,只須要使用正則匹配便可。settings.py 中有一個 ROOT_URLCONF 鍵來指定根 url.py,在這裏會包含你全部 app 的 urls.py 文件。若是沒有匹配成功,將會拋出一個異常 django.core.urlresolvers.Resolver404, 這是 django.http.HTTP404 的子類。
到這一步以後 WSGI handler 知道了調用哪個視圖函數,以及傳遞哪些參數。它會再一次調用中間件列表裏面的方法,此次是_view_middleware 列表。全部 Django 中間件的 process_view 方法將會被這樣聲明:
process_view(request, view_function, view_args, view_kwargs)
和 process_request 同樣,process_view 函數必須返回 None 或者 HTTPResponse 對象,使得 WSGI handler 繼續處理視圖或者’短路’處理流程並返回一個響應。在 CSRF middleware 中存在一個 process_view 的方法。做用是當 CSRF cookies 出現時,process_view 方法將會返回 None, 視圖函數將會繼續的執行。若是不是這樣,請求將會被拒絕,處理流程將會被’短路’,會生成一個錯誤的信息。
一個視圖函數須要知足三個條件:
若是視圖函數拋出一個異常,Handler 將會循環遍歷_exception_middleware 列表,這些方法按照相反的順序執行,從 settings.py 裏面列出來的最後一箇中間件到第一個。若是一個異常被拋出,處理過程將會被短路,其餘的 process_exception 將不會被執行。一般咱們依賴 Djnago's BaseHandler 提供的異常處理程序,可是咱們也可使用自定義的異常處理中間件。
在這個階段,咱們獲得了一個 HTTPResponse 對象,這個對象多是 process_view 返回的,也多是視圖函數返回的。如今咱們將循環訪問響應中間件。這是中間件調整數據的最後的機會。執行的順序是從內向外執行。 以 cache middleware 的 process_response 爲例:它依賴於你的 app 裏面的不一樣的狀態(緩存是否打開或者關閉,是否在處理一個數據流),來決定是否緩存你的響應。
django 1.10 和以前版本的區別: 在舊版本的 MIDDLEWARE_CLASSES 中,就算一箇中間件」短路」了執行過程,全部的中間件都會調用它們的 process_response 方法。而在新的 MIDDLEWARES 版本中,只有這個中間件和在它以前執行的中間件纔會調用 process_response 方法。
以上就是 django 在處理一個請求的基本的過程,最後 django 的 WSGI Handler 會建立一個來自 HTTPResponse 的返回值,並且會調用回調函數把數據傳遞給 web server, 最後返回給用戶。
如下是兩個關鍵點:
————————————————————————————————————— 關注微信公衆號:創宇前端(KnownsecFED),碼上獲取更多優質乾貨!