Django的請求到響應的處理流程本質上差不多,簡單來說,都是利用WSGI,針對request,進行response。當然在響應前會傳送request_started訊號,會呼叫預處理函式(在Flask中是before_request,Django是請求中介軟體,process_request),響應完成後會傳送request_finished函式,呼叫響應後函式(在Flask中是after_request中,Django中是process_response)。思想差不多,但是處理細節上還是有很多不同的。
- 使用者瀏覽器一個url,發起一個請求
在Web應用啟動後,會生成一個 WSGIHandler 例項(根據setting中的WSGI_APPLICATION = ‘dailyblog.wsgi.application’ 呼叫函式),每次請求響應都用這個例項。 settings 裡設定了 WSGI application。 返回例項程式碼:
1 2 3 4 5 6 7 8 9 10 11 |
from django.core.handlers.wsgi import WSGIHandler def get_wsgi_application(): """ The public interface to Django's WSGI support. Should return a WSGI callable. Allows us to avoid making django.core.handlers.WSGIHandler public API, in case the internal WSGI implementation changes or moves in the future. """ django.setup() return WSGIHandler() |
WSGIHandler繼承自BaseHander。這個處理器會匯入專案中的 settings模組、匯入 自定義例外類,最後呼叫自己的 load_middleware 方法,載入所有列在 MIDDLEWARE_CLASSES 中的 middleware 類並且內省它們。
BaseHander部分原始碼:
1 2 3 4 5 6 7 8 9 10 11 12 |
class BaseHandler(object): # Changes that are always applied to a response (in this order). response_fixes = [ http.conditional_content_removal, ] def __init__(self): self._request_middleware = None self._view_middleware = None self._template_response_middleware = None self._response_middleware = None self._exception_middleware = None |
請注意,只有第一次請求時會呼叫 load_middleware,以後不再呼叫了。
其中,request_middleware、view_middleware 是順序,template_response_middleware、response_middleware、exception_middleware 是逆序。
一 個 middleware 類可以包括請求響應過程的四個階段:request,view,response 和 exception。對應的方法:process_request,process_view, process_response 和 process_exception。我們在middleware中介軟體中定義其中的方法。
3、構造WSGIRequest。
WSGIHandler 處理器準備工作已經完成,隨後它給排程程式傳送一個訊號 request_started(這個和Flask中的request_started訊號差不多),然後根據入 environ 構造 WSGIRequest 物件,它的父類是HttpRequest。
4、 處理Middleware的request中介軟體
WSGIHander的get_response方法處理 _request_middleware 例項變數並呼叫其中的每一個方法,傳入 HttpRequest 的例項作為引數,即請求到達Request Middlewares,中介軟體對request做一些預處理,如果中介軟體返回response,會直接響應請求。
5、 URLConf通過urls.py檔案和請求的URL找到相應的檢視函式
此時會建立django.core.urlresolvers.RegexURLResolver 的一個例項。
URLresolver 遵循一個相當簡單的模式。對於在 URL 配置檔案中根據 ROOT_URLCONF 的配置產生的每一個在 urlpatterns 列表中的條目,它會檢查請 求的 URL 路徑是否與這個條目的正規表示式相匹配,如果是的話,有兩種選擇:
- 如果這個條目有一個可以呼叫的 include,resolver 擷取匹配的 URL,轉 到 include 指定的 URL 配置檔案並開始遍歷其中 urlpatterns 列表中的 每一個條目。根據你 URL 的深度和模組性,這可能重複好幾次。
- 否則,resolver 返回三個條目:匹配的條目指定的 view function;一個 從 URL 得到的未命名匹配組(被用來作為 view 的位置引數);一個關鍵 字引數字典,它由從 URL 得到的任意命名匹配組和從 URLConf 中得到的任 意其它關鍵字引數組合而成。
注意這一過程會在匹配到第一個指定了 view 的條目時停止,因此最好讓你的 URL 配置從複雜的正則過渡到簡單的正則,這樣能確保 resolver 不會首先匹配 到簡單的那一個而返回錯誤的 view function。
如果沒有找到匹配的條目,resolver 會產生 django.core.urlresolvers.Resolver404 異常,它是 django.http.Http404 例 外的子類。後面我們會知道它是如何處理的。
6、 開始呼叫View中相應的檢視函式或基於類的檢視。
7、View進行一些處理,如通過模型Models返回資料。
8、如果需要,Views可以建立一個額外的Context,Context被當做變數傳給Template。
9、Template渲染輸出
10、渲染後的輸出被返回到View
11、HTTPResponse被髮送到Response Middlewares
12、Response Middlewares對response進行特定的處理,然後返回一個新的response
13、Response返回呈現給使用者
14、一旦 middleware完成了最後環節,處理器將傳送一個訊號 request_finished,訂閱這個訊號的事件會清空並釋放任何使用中的資源。比如,Django 的 request_finished 的訂閱者subscriber會關閉所有資料庫連線。
15、所有流程至此已經全部完成。
下面是處理器的原始碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class WSGIHandler(base.BaseHandler): initLock = Lock() request_class = WSGIRequest #建立 WSGIRequest def __call__(self, environ, start_response): # Set up middleware if needed. We couldn't do this earlier, because # settings weren't available. if self._request_middleware is None: with self.initLock: try: # Check that middleware is still uninitialized. if self._request_middleware is None: self.load_middleware() except: # Unload whatever middleware we got self._request_middleware = None raise set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) try: request = self.request_class(environ) except UnicodeDecodeError: logger.warning('Bad Request (UnicodeDecodeError)', exc_info=sys.exc_info(), extra={ 'status_code': 400, } ) response = http.HttpResponseBadRequest() else: response = self.get_response(request) response._handler_class = self.__class__ status = '%s %s' % (response.status_code, response.reason_phrase) response_headers = [(str(k), str(v)) for k, v in response.items()] for c in response.cookies.values(): response_headers.append((str('Set-Cookie'), str(c.output(header='')))) start_response(force_str(status), response_headers) if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'): response = environ['wsgi.file_wrapper'](response.file_to_stream) return respons |
異常情況:
如果 view 函式,或者其中的什麼東西,發生了異常,那麼 get_response將遍歷它的 _exception_middleware 例項變數並呼叫那裡的每個方法,傳入 HttpResponse 和這個 exception 作為引數。如果順利,最後會例項化,並返回一個 HttpResponse 。
這時候有可能還是沒有得到一個 HttpResponse,這可能有幾個原因:
- view 並沒有返回內容。
- view 可能丟擲了異常,但middleware沒有 能處理它。
- 一個 middleware 方法試圖處理一個異常時,自己又產生了一個異常。
處理器會做些相應的異常處理,如404,403,500等等,具體可以看上面的原始碼。
裡面有些地方是參考其他部落格文章,但忘了地址了。如有原作者看到,請給予提示,謝謝。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式