根据 Django runserver 源码流程分析 我们知道用 python manage.py runserver 0.0.0.0:8080 启动服务后,Django 对每一个请求都会启动一个线程去执行 C:\Python27\Lib\site-packages\django\core\wsgi.py 中的函数 get_wsgi_application 返回的类 WSGIHandler 的对象即就是调用该对象的__call__方法。那么__call__方法的执行流程是什么呢?
就是本文要解释的问题即分析一个具体请求的生命周期以及在该周期中 Django 框架的各个中间件的 process_request 、 process_view 、process_response 是怎么参与的。
首先浏览器发起请求:当请求到 Django 框架的时候,
根据中间件的注册顺序依次执行:
-
根据注册顺序执行所有中间件的 process_request:
[process_request, process_request, process_request, process_request, process_request] -
根据注册顺序执行所有中间件的 process_view:
[process_view, process_view, process_view, process_view, process_view] -
执行根据 URL 找到的视图函数
-
根据注册的顺序逆序执行所有中间件的 process_response:
[process_response, process_response, process_response, process_response, process_response]
另外扩展了发送 request_started、request_finished 信号的时机
这个可以补充说明:Django csrf 的源码解析的扩展1
源码分析:
D:\WorkSpace\HelloNG\src\testcode\djangoTest\robappdj\robappdj\wsgi.py:
application = get_wsgi_application() ----------------------------------------------step 1
C:\Python27\Lib\site-packages\django\core\wsgi.py
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() -----------------------------------------------------------step2
C:\Python27\Lib\site-packages\django\core\handlers\wsgi.py:
class WSGIHandler(base.BaseHandler):
initLock = Lock()
request_class = 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) -----step3: 发送 request_started 的信号
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) -------------------------------step4
C:\Python27\Lib\site-packages\django\core\handlers\base.py:
class BaseHandler(object):
# Changes that are always applied to a response (in this order).
response_fixes = [
http.fix_location_header,
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
def get_response(self, request):
"Returns an HttpResponse object for the given HttpRequest"
# Setup default url resolver for this thread, this code is outside
# the try/except so we don't get a spurious "unbound local
# variable" exception in the event an exception is raised before
# resolver is set
urlconf = settings.ROOT_URLCONF
urlresolvers.set_urlconf(urlconf)
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
try:
response = None
# Apply request middleware
for middleware_method in self._request_middleware: ------------------------step5: process_request
response = middleware_method(request)
if response:
break
if response is None:
if hasattr(request, 'urlconf'):
# Reset url resolver with a custom urlconf.
urlconf = request.urlconf
urlresolvers.set_urlconf(urlconf)
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match
# Apply view middleware
for middleware_method in self._view_middleware: ------------------------step6: process_view
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
break
if response is None:
wrapped_callback = self.make_view_atomic(callback) -----------------------step7: 根据 URL 找到的视图函数
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs)--step7_1: 就是视图函数返回的 HttpResponse 对象
except Exception as e:
# If the view raised an exception, run it through exception
# middleware, and if the exception middleware returns a
# response, use that. Otherwise, reraise the exception.
for middleware_method in self._exception_middleware:
response = middleware_method(request, e)
if response:
break
if response is None:
raise
# 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
if 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__))
response = response.render()
except http.Http404 as e:
logger.warning('Not Found: %s', request.path,
extra={
'status_code': 404,
'request': request
})
if settings.DEBUG:
response = debug.technical_404_response(request, e)
else:
response = self.get_exception_response(request, resolver, 404)
except PermissionDenied:
logger.warning(
'Forbidden (Permission denied): %s', request.path,
extra={
'status_code': 403,
'request': request
})
response = self.get_exception_response(request, resolver, 403)
except MultiPartParserError:
logger.warning(
'Bad request (Unable to parse request body): %s', request.path,
extra={
'status_code': 400,
'request': request
})
response = self.get_exception_response(request, resolver, 400)
except SuspiciousOperation as e:
# The request logger receives events for any problematic request
# The security logger receives events for all SuspiciousOperations
security_logger = logging.getLogger('django.security.%s' %
e.__class__.__name__)
security_logger.error(
force_text(e),
extra={
'status_code': 400,
'request': request
})
if settings.DEBUG:
return debug.technical_500_response(request, *sys.exc_info(), status_code=400)
response = self.get_exception_response(request, resolver, 400)
except SystemExit:
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
raise
except: # Handle everything else.
# Get the exception info now, in case another exception is thrown later.
signals.got_request_exception.send(sender=self.__class__, request=request)
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
try:
# Apply response middleware, regardless of the response
for middleware_method in self._response_middleware: ------------------------step8: process_response
response = middleware_method(request, response)
# Complain if the response middleware returned None (a common error).
if response is None:
raise ValueError(
"%s.process_response didn't return an "
"HttpResponse object. It returned None instead."
% (middleware_method.__self__.__class__.__name__))
response = self.apply_response_fixes(request, response)
except: # Any exception should be gathered and handled
signals.got_request_exception.send(sender=self.__class__, request=request)
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
response._closable_objects.append(request)
return response
D:...\db_operator\tests.py
from django.http import HttpResponse
def test(request):
logger.info('In the test')
time.sleep(10)
logger.info("before ORMMMM")
obj = DataBaseInfo.objects.filter(id=1).update(update_datetime='2020-09-30T14:49:05.244599Z')
logger.info('obj = {0}'.format(obj))
time.sleep(10)
return HttpResponse('hello')
D:...\venv\Lib\site-packages\django\http\response.py
class HttpResponse(HttpResponseBase):
......
class HttpResponseBase(six.Iterator):
# The WSGI server must call this method upon completion of the request. -----------------------step7_2: 这个注释很到位
# See http://blog.dscpl.com.au/2012/10/obligations-for-calling-close-on.html
def close(self):
for closable in self._closable_objects:
try:
closable.close()
except Exception:
pass
self.closed = True
signals.request_finished.send(sender=self._handler_class) -----------------------step7_3: 发送 request_finished 的信号,
具体 close 什么时候调用参考 2_1python_manage_runserver.txt
D:...\venv\Lib\site-packages\django\db\__init__.py
# Register an event to reset saved queries when a Django request is started.
def reset_queries(**kwargs):
for conn in connections.all():
conn.queries_log.clear()
signals.request_started.connect(reset_queries)
# Register an event to reset transaction state and close connections past
# their lifetime.
def close_old_connections(**kwargs):
for conn in connections.all():
conn.close_if_unusable_or_obsolete()
signals.request_started.connect(close_old_connections) -----------------------step7_3: 信号 request_started 和 request_finished 的接受者
signals.request_finished.connect(close_old_connections)