本文所用 Django
代碼版本:2.1.3
python
本文中進行的分析並不侷限於某一個 Django
版本但都會盡可能討論版本 2.0+
django
Django
和其餘 Web
框架的 HTTP
處理的流程大體相同:先經過 Request Middleware
對請求對象作定義處理,而後再經過默認的 URL
指向的方法,最後再經過 Response Middleware
對響應對象作自定義處理。瀏覽器
Django
建立 WSGIServer
類的實例Django
頁面Django WSGIServer
接收客戶端(瀏覽器)請求初始化 WSGIHandler
實例setting
配置和 Django
異常類setting
中設置的中間件_request_middleware,_view_middleware,_response_middleware,_exception_middleware
四個列表_request_middleware
,對 request
進行處理:若返回 None
進入到 8;若直接返回 HttpResponse
對象進入到 12url
並進行匹配(假設匹配成功)_view_middleware
,對 request
進行處理:若返回 None
進入到 10;若直接返回 HttpResponse
對象進入到 12。url
匹配的 view
邏輯:若引起異常進入到 11;若正常返回 HttpResponse
對象進入到 12_exception_middleware
_response_middleware
,對 HttpResponse
進行處理並最終返回 response
在開發環境中,咱們通常是經過命令行執行 runserver
命令,ruserver
命令是使用 Django
自帶的的 Web Server
,而在正式的環境中,通常會使用 Nginx+uWSGI
模式。bash
不管經過哪一種方式,啓動一個項目時,都會作兩件事:服務器
WSGIServer
類的實例,來接受用戶的請求。HTTP
請求到達的時,爲用戶指定一個 WSGIHandler
,用於處理用戶請求與響應,這個 Handler
是處理整個 Request
的核心。WSGI
:全稱 Web Server Gateway Interface
。cookie
WSGI
不是服務器,Python
模塊,框架,API
或者任何軟件,只是一種規範,描述 Web Server
如何與 Web Application
通訊的規範。架構
WSGI
協議主要包括 server
和 application
兩部分:app
WSGI Server
負責從客戶端接收請求,將 request
轉發給 application
,將application
返回的 response
返回給客戶端;WSGI Application
接收由 server
轉發的 request
,處理請求,並將處理結果返回給 server
。application
中能夠包括多個棧式的中間件(middlewares),這些中間件須要同時實現 server
與 application
,所以能夠在 WSGI
服務器與 WSGI
應用之間起調節做用:對服務器來講,中間件扮演應用程序,對應用程序來講,中間件扮演服務器。WSGI Application
應該實現爲一個可調用對象,例如:函數、方法、類(包含 call
方法)。框架
須要接收兩個參數:socket
environment
(編碼中多簡寫爲 environ
、env
);HTTP
響應狀態(HTTP Status
)、響應頭(HTTP Headers
)的回調函數;經過回調函數將響應狀態和響應頭返回給 WSGI Server
,同時返回響應正文,響應正文是可迭代的、幷包含了多個字符串。下面是 Django WSGI Application
的具體實現:
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 本文做者注:加載中間件
self.load_middleware()
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
# 本文做者注:處理請求前發送信號
signals.request_started.send(sender=self.__class__, environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
response._handler_class = self.__class__
status = '%d %s' % (response.status_code, response.reason_phrase)
response_headers = list(response.items())
for c in response.cookies.values():
response_headers.append(('Set-Cookie', c.output(header='')))
# 本文做者注:將響應的 header 和 status 返回給 server
start_response(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 response
複製代碼
能夠看出 Django WSGI Application
的流程包括:
get_response()
方法處理當前請求,該方法的的主要邏輯是經過urlconf
找到對應的 view
和 callback
,按順序執行各類 middleware
和 callback
;WSGI Server
傳入的 start_response()
方法將響應 header
與 status
返回給 WSGI Server
;負責獲取 HTTP
請求,將請求傳遞給 Django WSGI Application
,由 Django WSGI Application
處理請求後返回 response
。以 Django
內建 server
爲例看一下具體實現。
經過 runserver
命令運行 Django
項目,在啓動時都會調用下面的 run
方法,建立一個 WSGIServer
的實例,以後再調用其 serve_forever()
方法啓動服務。
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
server_address = (addr, port)
if threading:
httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
else:
httpd_cls = server_cls
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
if threading:
httpd.daemon_threads = True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
複製代碼
下圖表示 WSGI Server
服務器處理流程中關鍵的類和方法(來自:參考引用_1)
run()
方法會建立 WSGIServer
實例,主要做用是接收客戶端請求,將請求傳遞給WSGI Application
,而後將 WSGI Application
返回的 response
返回給客戶端。
HTTP
請求的 handler
:WSGIRequestHandler
類;set_app
和 get_app
方法設置和獲取 WSGIApplication
實例wsgi_handler
;HTTP
請求時,調用 handler_request
方法,會建立 WSGIRequestHandler
實例處理 HTTP
請求;WSGIServer
中 get_request
方法經過 socket
接受請求數據;WSGIServer
在調用 handle_request
時建立實例,傳入 request,cient_address,WSGIServer
三個參數,__init__
方法在實例化同時還會調用自身的 handle
方法;handle
方法會建立 ServerHandler
實例,而後調用其 run
方法處理請求;WSGIRequestHandler
在其 handle
方法中調用 run
方法,傳入self.server.get_app()
參數,獲取 WSGIApplication
,而後調用實例(call),獲取 response
,其中會傳入 start_response
回調,用來處理返回的 header
和 status
;application
獲取 response
之後,經過 finish_response
返回 response
;WSGI
協議中的 application
,接收兩個參數,environ
字典包含了客戶端請求的信息以及其餘信息,能夠認爲是請求上下文,start_response
用於發送返回 status
和 header
的回調函數雖然上面一個 Django WSGI Server
涉及到多個類實現以及相互引用,但其實原理仍是調用WSGIHandler
,傳入請求參數以及回調方法 start_response()
,並將響應返回給客戶端。
在 Python3.7
的源碼中給出了一個 simple_server
案例位於 python3.7/wsgiref/simple_server.py
。
模塊實現了一個簡單的 HTTP
服務器,並給出了一個簡單的 demo
,能夠直接運行,運行結果會將請求中涉及到的環境變量在瀏覽器中展現出來。其中包括上述描述的整個 HTTP
請求的全部組件:ServerHandler,WSGIServer,WSGIRequestHandler
以及 demo_app
表示的簡易版的 WSGIApplication
。
感興趣的話能夠本身去看一下源碼。
Django
的配置都在 {project_name}/settings.py
中定義,能夠是 Django
的配置,也能夠是自定義的配置,而且都經過 django.conf.settings
訪問。
Django
中的 Middleware
相似底層中一個輕量級的插件系統,它可以介入 Django
的請求和響應過程,在全局修改 Django
的輸入和輸出內容。從流程總覽圖中能夠看出 Django
請求處理過程的核心在於 Middleware
,Django
中全部的請求和響應都有 Middleware
的參與。
一個 HTTP
請求,首先被轉化成一個 HttpRequest
對象,而後該對象被傳遞給 Request Middleware
處理,若是它返回了 HttpResponse
對象,則直接傳遞給 Response Middleware
作收尾處理。不然的話 Request Middleware
將訪問 URL
配置,肯定目標 view
來處理 HttpRequest
對象,在肯定了 view
,可是尚未執行時候,系統會把 HttpRequest
對象傳遞給 View Middleware
進行處理,若是它返回了 HttpResponse
對象,那麼該 HttpResponse
對象將被傳遞給 Response Middleware
進行後續處理,不然將執行肯定的 view
函數處理並返回 HttpResponse
對象,在整個過程當中若是引起了異常並拋出,會被 Exception Middleware
進行處理。
在請求階段,調用視圖以前,Django
按照 setting.py
設置的順序,自頂向下應用遍歷執行 Request Middleware
。你能夠把它想象成一個洋蔥:每一箇中間件類都是一個「層」,它覆蓋了洋蔥的核心。若是請求經過洋蔥的全部層(每個調用 get_response
)以將請求傳遞到下一層,一直到內核的視圖,那麼響應將在返回的過程當中經過每一個層(以相反的順序)。
編寫一個本身的中間件是很容易的,每一箇中間件組件都是一個獨立的 Python Class
,你能夠在自定義的 Class
下編寫一個或多個下面的方法:
函數樣式:process_request(request)
;
參數解析:request
是一個 HTTPRequest
對象;
調用時間:在 Django
決定執行哪一個 view
以前,process_request()
會被請求調用;
產生響應:它應該返回一個 None
或一個 HttpResponse
對象,若是返回 None
,Django
會繼續處理這個請求;若是它返回一個 HTTPResponse
對象,Django
會直接跳轉到 Response Middleware
;
函數樣式:process_view(request, view_func, view_args, view_kwargs)
;
參數解析:request
是一個 HTTPRequest
對象,view_func
是 Django
會調用的一個函數(準確的說是一個函數對象而非一個表示函數名的字符串),view_args
是一個會被傳遞到視圖的 *args
,view_kwargs
是一個會被傳遞到視圖的 **kwargs
,view_args
和 view_kwargs
都不包括 request
;
調用時間:process_view()
會在 Django
調用 view
前被調用;
產生響應:它應該返回一個 None
或一個 HttpResponse
對象,若是返回 None
,Django
會繼續處理這個請求;若是它返回一個 HTTPResponse
對象,Django
會直接跳轉到 Response Middleware
;
PS:除 CsrfViewMiddleware
外中間件運行時在視圖運行前或在 process_view()
中訪問 request.POST
會使得以後的全部視圖沒法修改 request
,因此應該儘可能避免。
函數樣式:process_template_response(request, response)
;
參數解析:request
是一個 HttpRequest
對象,response
是一個 TemplateResponse
對象(或相似對象),由 Django
視圖或中間件返回;
調用時間:若是 response
的實例有 render()
方法,process_template_response()
在視圖恰好執行完畢以後被調用,這代表他是一個 TemplateResponse
對象(或相似對象);
產生響應:這個方法必須返回一個實現了 render()
方法的 TemplateResponse
對象(或相似對象),它能夠修改給定的 response
對象,也能夠建立一個全新的 TemplateResponse
對象(或相似對象);
PS:在響應處理階段,中間件以相反的順序運行,包括 process_template_response
;
函數樣式:process_response(request, response)
;
參數解析:request
是一個 HttpRequest
對象,response
是一個 HttpResponse
對象,由 Django
視圖或中間件返回;
調用時間:process_request
在全部響應返回客戶端前被調用;
產生響應:這個方法必須返回一個 HttpRequest
對象,它能夠修改給定的 response
對象,也能夠建立一個全新的 HttpRequest
對象;
PS:process_response
老是被調用,這意味着你的 process_response
不能依賴 process_request
函數樣式:process_exception(request, exception)
;
參數解析:request
是一個 HttpRequest
對象,exception
是一個被視圖拋出 Exception
對象;
調用時間:當一個視圖拋出異常,Django
會調用 process_exception
來處理;
產生響應:它應該返回一個 None
或一個 HttpResponse
對象,若是返回 None
,Django
會繼續處理這個請求;若是它返回一個 HTTPResponse
對象,模板對象和 Response Middleware
會被直接返回給客戶端;不然,將啓動默認異常處理。;
假設:中間件便利執行完 _request_middleware,_view_middleware
後都返回 None
。
當 Django
遍歷執行完 _request_middleware
後會獲得一個通過處理的 request
對象,此時 Django
將按順序進行對 url
進行正則匹配,若是匹配不成功,就會拋出異常;若是匹配成功,Django
會繼續循環執行 _view_middleware
並在執行後繼續執行剛剛匹配成功的 view
。
在 setting
中有一個 ROOT_URLCONF
,它指向 urls.py
文件,根據這個文件能夠生產一個 urlconf
,本質上,他就是 url
與視圖函數之間的映射表,而後經過 resolver
解析用戶的 url
,找到第一個匹配的 view
。
重要函數源碼位置:
_path: django/urls/conf.py
URLPattern: django/urls/resolvers.py
ResolverMatch: django/urls/resolvers.py
URLResolver: django/urls/resolvers.py
複製代碼
源碼比較長,就不放出來了,感興趣的話本身去看吧。
urlpatterns
的配置執行 _path
函數;_path
函數進行判斷:若是是一個 list
或者 tuple
,就用 URLResolver
處理,跳至 4;若是是一個正常的可調用的 view
函數,則用 URLPattern
處理,跳至;若是匹配失敗,拋出異常;URLPattern
初始化相應值後執行 resolve
方法:若是匹配成功,返回 ResolverMatch
;若是匹配失敗,拋出異常;URLResolver
匹配 path
若是匹配成功,則繼續匹配它的 url_patterns
,跳至 5;匹配失敗,拋出異常;url_patterns
:若爲 urlpattern
匹配成功,返回 ResolverMatch
;若爲 URLResolver
遞歸調用 URLResolver
跳至 4;若匹配失敗,拋出異常;能夠發現,整個過程的關鍵就是 ResolverMatch
,URLPattern
和 URLResolver
三個類,其中: ResolverMatch
是匹配結果,包含匹配成功後須要的信息; URLPattern
是一個 url
映射信息的對象,包含了 url
映射對應的可調用對象等信息; URLResolver
是實現 url
路由,解析 url
的關鍵的地方,它的 url_patterns
既能夠是URLPattern
也能夠是 URLResolver
,正是由於這種設計, 實現了對 url
的層級解析。
真實的請求響應過程確定是比我提到的這些還要複雜的多,可是個人能力實在有限,目前僅能理解到這個層面了,若是錯誤歡迎指正。
參考引用: