Django请求生命周期的源码分析:中间件的process_request 、 process_view 、process_response的执行顺序和请求信号的发送

根据 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 框架的时候,
根据中间件的注册顺序依次执行:

  1. 根据注册顺序执行所有中间件的 process_request:
    [process_request, process_request, process_request, process_request, process_request]

  2. 根据注册顺序执行所有中间件的 process_view:
    [process_view, process_view, process_view, process_view, process_view]

  3. 执行根据 URL 找到的视图函数

  4. 根据注册的顺序逆序执行所有中间件的 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)	
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值