web应用或者网站本质上都是围绕着请求-响应的方式来运作的。当你通过浏览器访问网站时,浏览器会向web服务器发送请求。当web服务器收到请求后,服务器会对请求进行相应的处理,然后返回相应的响应给浏览器,最后浏览器呈现给你。
毫无意外,Django应用也是如此。它也需要给用户发送的请求返回相应的响应。接着我将会通过分析Django框架的代码来解释Django的请求响应流程是如何的。
代码分析
Django应用最开始的入口处在wsgi.py
,这里是从web服务器转发请求到Django应用的地方,同时也是Django应用返回响应给web服务器的地方。
# 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)
# 根据web服务器传入的参数,初始化request请求
request = self.request_class(environ)
# Django开始处理请求,生成响应
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 协议,调用start_response,然后给web服务器
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
是引导Django应用运用的地方,从这里才开始能真正处理请求以及返回响应给web服务器。具体的操作相关的代码在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已经没有用处,这不表示请求中间件和响应中间件没用,只是用了新的机制来应用
self._request_middleware = []
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []
#将self._get_response包装在异常处理内,此时的handler是一个包装了_get_response的修饰器函数,进行了异常处理。
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类中间件的链表进行了处理。
'''
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进行了处理。对handler进行了一层包装。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就是一个修饰器函数,他最内层是真正的_get_response,外层则是哪些中间件
self._middleware_chain = handler
可以看到self.load_middleware()
是加载中间件的过程。
可以看到主循环是对配置文件里中间件进行逆序遍历,_view_middleware
链表是执行的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种,则是相反的顺序。
继续看self.get_response(request)
,看看是如何处理请求,生成响应的。
#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
# 根据settings.ROOT_URLCONF设置urlconf,初始化路由
set_urlconf(settings.ROOT_URLCONF)
# 调用包装了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处理请求调用的函数,最关键的就是_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中间件包装在最里面的。所以这里面就是实际调用具体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
# 先调用view中间件
for middleware_method in self._view_middleware:
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
break
# 这里调用匹配到的具体的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,然后调用view的过程。通过url匹配具体的view的原理后面再写一篇文章来介绍。
总结
通过上面代码分析,我们已经大致了解了Django请求响应的流程。大致如下
用户请求首先会到web服务器;
web服务器会把请求发到django.core.handlers.wsgi
的BaseHandler
;
生成request,response,view, exception,template_response中间件链表;
按中间件配置顺序应用request中间件来处理request,如果这中间生成response,则直接返回;
通过urlresolvers.resolve
匹配请求的url来找到对应的view;
应用view中间件,如果有response,则直接返回;
调用对应的view,这个过程和和models进行交互,比如从数据库获取数据等,并渲染模板;
接着response中间件会被应用来处理repsonse;
这其中忽略了一些其他重要的步骤,比如异常中间件的调用。
最后放一张网上的图,我觉得画的比较形象