web應(yīng)用或者網(wǎng)站本質(zhì)上都是圍繞著請求-響應(yīng)的方式來運作的婉烟。當(dāng)你通過瀏覽器訪問網(wǎng)站時,瀏覽器會向web服務(wù)器發(fā)送請求设江。當(dāng)web服務(wù)器收到請求后锦茁,服務(wù)器會對請求進(jìn)行相應(yīng)的處理,然后返回相應(yīng)的響應(yīng)給瀏覽器叉存,最后瀏覽器呈現(xiàn)給你码俩。
毫無意外,Django應(yīng)用也是如此歼捏。它也需要給用戶發(fā)送的請求返回相應(yīng)的響應(yīng)稿存。接著我將會通過分析Django框架的代碼來解釋Django的請求響應(yīng)流程是如何的。
代碼分析
Django應(yīng)用最開始的入口處在wsgi.py
瞳秽,這里是從web服務(wù)器轉(zhuǎn)發(fā)請求到Django應(yīng)用的地方瓣履,同時也是Django應(yīng)用返回響應(yīng)給web服務(wù)器的地方。
# django/core/handlers/wsgi.py
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)
# 根據(jù)web服務(wù)器傳入的參數(shù)练俐,初始化request請求
request = self.request_class(environ)
# Django開始處理請求袖迎,生成響應(yīng)
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='')))
# wsgi 協(xié)議,調(diào)用start_response痰洒,然后給web服務(wù)器
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
上面這段代碼可以看到WSGIHandler
是引導(dǎo)Django應(yīng)用運用的地方瓢棒,從這里才開始能真正處理請求以及返回響應(yīng)給web服務(wù)器。具體的操作相關(guān)的代碼在base.BaseHandler
丘喻,先去base.BaseHandler
深入分析self.load_middleware()
加載中間件的過程.
#django/core/handlers/base.py
class BaseHandler:
_request_middleware = None
_view_middleware = None
_template_response_middleware = None
_response_middleware = None
_exception_middleware = None
_middleware_chain = None
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE.
Must be called after the environment is fixed (see __call__ in subclasses).
"""
#中間件鏈表脯宿,這其中的_request_middleware和_response_middleware已經(jīng)沒有用處,這不表示請求中間件和響應(yīng)中間件沒用泉粉,只是用了新的機(jī)制來應(yīng)用
self._request_middleware = []
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []
#將self._get_response包裝在異常處理內(nèi)连霉,此時的handler是一個包裝了_get_response的修飾器函數(shù),進(jìn)行了異常處理嗡靡。
handler = convert_exception_to_response(self._get_response)
# 遍歷settings配置文件中的中間件
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
try:
mw_instance = middleware(handler)
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if str(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
if mw_instance is None:
raise ImproperlyConfigured(
'Middleware factory %s returned None.' % middleware_path
)
'''
可以看到跺撼,這里只有對_view_middleware,_template_response_middleware讨彼,_exception_middleware這3類中間件的鏈表進(jìn)行了處理歉井。
'''
if hasattr(mw_instance, 'process_view'):
self._view_middleware.insert(0, mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.append(mw_instance.process_template_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.append(mw_instance.process_exception)
# 是這里對_request_middleware和_response_middleware進(jìn)行了處理。對handler進(jìn)行了一層包裝哈误。handler = convert_exception_to_response(middleware(handler))
handler = convert_exception_to_response(mw_instance)
# We only assign to this when initialization is complete as it is used
# as a flag for initialization being complete.
# 所以這里的_middleware_chain就是一個修飾器函數(shù)哩至,他最內(nèi)層是真正的_get_response躏嚎,外層則是哪些中間件
self._middleware_chain = handler
可以看到self.load_middleware()
是加載中間件的過程。
可以看到主循環(huán)是對配置文件里中間件進(jìn)行逆序遍歷菩貌,_view_middleware
鏈表是執(zhí)行的insert(0
操作卢佣,所以這個鏈接最后的順序是和配置文件的順序相同。而_template_response_middleware
和_exception_middleware
都是append
的操作箭阶,所以最后生成的鏈表順序是和配置文件的順序相反虚茶。這里面比較特殊的是_request_middleware
和_response_middleware
,并沒有使用其他3種中間件那樣的鏈表仇参,而是用了包裝器嘹叫,外層的中間件包裝著里面的中間件,最里面的是_get_response
冈敛。其實這兩類中間件都會繼承自MiddlewareMixin
待笑,看完它就一目了然了鸣皂。
# django/utils/deprecation.py
class MiddlewareMixin:
def __init__(self, get_response=None):
self.get_response = get_response
super().__init__()
def __call__(self, request):
response = None
# 處理request中間件
if hasattr(self, 'process_request'):
response = self.process_request(request)
response = response or self.get_response(request)
# 處理response中間件
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
所以這里我們可以看出大致流程抓谴,request中間件和view中間件的處理順序和配置順序相同,另外3種寞缝,則是相反的順序癌压。
繼續(xù)看self.get_response(request)
,看看是如何處理請求荆陆,生成響應(yīng)的滩届。
#django/core/handlers/base.py
def get_response(self, request):
"""Return an HttpResponse object for the given HttpRequest."""
# Setup default url resolver for this thread
# 根據(jù)settings.ROOT_URLCONF設(shè)置urlconf,初始化路由
set_urlconf(settings.ROOT_URLCONF)
# 調(diào)用包裝了self._get_response的_middleware_chain
response = self._middleware_chain(request)
response._closable_objects.append(request)
# If the exception handler returns a TemplateResponse that has not
# been rendered, force it to be rendered.
if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
response = response.render()
if response.status_code == 404:
logger.warning(
'Not Found: %s', request.path,
extra={'status_code': 404, 'request': request},
)
return response
這里是WSGIHandler處理請求調(diào)用的函數(shù)被啼,最關(guān)鍵的就是_middleware_chain(request)
, 他是被中間件包裝了的_get_response的handler帜消。最后看self._get_response
.
#django/core/handlers/base.py
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything that happens
inside the request/response middleware.
如注釋說的,_get_response是被request/response中間件包裝在最里面的浓体。所以這里面就是實際調(diào)用具體view的地方了泡挺。
"""
response = None
if hasattr(request, 'urlconf'):
urlconf = request.urlconf
set_urlconf(urlconf)
resolver = get_resolver(urlconf)
else:
resolver = get_resolver()
# 通過request的url來匹配得到具體的view
resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match
# Apply view middleware
# 先調(diào)用view中間件
for middleware_method in self._view_middleware:
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
break
# 這里調(diào)用匹配到的具體的view
if response is None:
wrapped_callback = self.make_view_atomic(callback)
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs)
except Exception as e:
response = self.process_exception_by_middleware(e, request)
# Complain if the view returned None (a common error).
if response is None:
if isinstance(callback, types.FunctionType): # FBV
view_name = callback.__name__
else: # CBV
view_name = callback.__class__.__name__ + '.__call__'
raise ValueError(
"The view %s.%s didn't return an HttpResponse object. It "
"returned None instead." % (callback.__module__, view_name)
)
# If the response supports deferred rendering, apply template
# response middleware and then render the response
elif hasattr(response, 'render') and callable(response.render):
for middleware_method in self._template_response_middleware:
response = middleware_method(request, response)
# Complain if the template response middleware returned None (a common error).
if response is None:
raise ValueError(
"%s.process_template_response didn't return an "
"HttpResponse object. It returned None instead."
% (middleware_method.__self__.__class__.__name__)
)
try:
response = response.render()
except Exception as e:
response = self.process_exception_by_middleware(e, request)
return response
上面的self._get_response
就是通過請求的url匹配具體的view,然后調(diào)用view的過程命浴。通過url匹配具體的view的原理后面再寫一篇文章來介紹娄猫。
總結(jié)
通過上面代碼分析,我們已經(jīng)大致了解了Django請求響應(yīng)的流程生闲。大致如下
用戶請求首先會到web服務(wù)器媳溺;
web服務(wù)器會把請求發(fā)到django.core.handlers.wsgi
的BaseHandler
;
生成request碍讯,response悬蔽,view, exception捉兴,template_response中間件鏈表蝎困;
按中間件配置順序應(yīng)用request中間件來處理request缅帘,如果這中間生成response,則直接返回难衰;
通過urlresolvers.resolve
匹配請求的url來找到對應(yīng)的view钦无;
應(yīng)用view中間件,如果有response盖袭,則直接返回失暂;
調(diào)用對應(yīng)的view,這個過程和和models進(jìn)行交互鳄虱,比如從數(shù)據(jù)庫獲取數(shù)據(jù)等弟塞,并渲染模板;
接著response中間件會被應(yīng)用來處理repsonse拙已;
這其中忽略了一些其他重要的步驟决记,比如異常中間件的調(diào)用。
最后放一張網(wǎng)上的圖倍踪,我覺得畫的比較形象