You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

base.py 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import logging
  2. import types
  3. from django.conf import settings
  4. from django.core.exceptions import ImproperlyConfigured, MiddlewareNotUsed
  5. from django.core.signals import request_finished
  6. from django.db import connections, transaction
  7. from django.urls import get_resolver, set_urlconf
  8. from django.utils.log import log_response
  9. from django.utils.module_loading import import_string
  10. from .exception import convert_exception_to_response
  11. logger = logging.getLogger('django.request')
  12. class BaseHandler:
  13. _view_middleware = None
  14. _template_response_middleware = None
  15. _exception_middleware = None
  16. _middleware_chain = None
  17. def load_middleware(self):
  18. """
  19. Populate middleware lists from settings.MIDDLEWARE.
  20. Must be called after the environment is fixed (see __call__ in subclasses).
  21. """
  22. self._view_middleware = []
  23. self._template_response_middleware = []
  24. self._exception_middleware = []
  25. handler = convert_exception_to_response(self._get_response)
  26. for middleware_path in reversed(settings.MIDDLEWARE):
  27. middleware = import_string(middleware_path)
  28. try:
  29. mw_instance = middleware(handler)
  30. except MiddlewareNotUsed as exc:
  31. if settings.DEBUG:
  32. if str(exc):
  33. logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
  34. else:
  35. logger.debug('MiddlewareNotUsed: %r', middleware_path)
  36. continue
  37. if mw_instance is None:
  38. raise ImproperlyConfigured(
  39. 'Middleware factory %s returned None.' % middleware_path
  40. )
  41. if hasattr(mw_instance, 'process_view'):
  42. self._view_middleware.insert(0, mw_instance.process_view)
  43. if hasattr(mw_instance, 'process_template_response'):
  44. self._template_response_middleware.append(mw_instance.process_template_response)
  45. if hasattr(mw_instance, 'process_exception'):
  46. self._exception_middleware.append(mw_instance.process_exception)
  47. handler = convert_exception_to_response(mw_instance)
  48. # We only assign to this when initialization is complete as it is used
  49. # as a flag for initialization being complete.
  50. self._middleware_chain = handler
  51. def make_view_atomic(self, view):
  52. non_atomic_requests = getattr(view, '_non_atomic_requests', set())
  53. for db in connections.all():
  54. if db.settings_dict['ATOMIC_REQUESTS'] and db.alias not in non_atomic_requests:
  55. view = transaction.atomic(using=db.alias)(view)
  56. return view
  57. def get_response(self, request):
  58. """Return an HttpResponse object for the given HttpRequest."""
  59. # Setup default url resolver for this thread
  60. set_urlconf(settings.ROOT_URLCONF)
  61. response = self._middleware_chain(request)
  62. response._closable_objects.append(request)
  63. if response.status_code >= 400:
  64. log_response(
  65. '%s: %s', response.reason_phrase, request.path,
  66. response=response,
  67. request=request,
  68. )
  69. return response
  70. def _get_response(self, request):
  71. """
  72. Resolve and call the view, then apply view, exception, and
  73. template_response middleware. This method is everything that happens
  74. inside the request/response middleware.
  75. """
  76. response = None
  77. if hasattr(request, 'urlconf'):
  78. urlconf = request.urlconf
  79. set_urlconf(urlconf)
  80. resolver = get_resolver(urlconf)
  81. else:
  82. resolver = get_resolver()
  83. resolver_match = resolver.resolve(request.path_info)
  84. callback, callback_args, callback_kwargs = resolver_match
  85. request.resolver_match = resolver_match
  86. # Apply view middleware
  87. for middleware_method in self._view_middleware:
  88. response = middleware_method(request, callback, callback_args, callback_kwargs)
  89. if response:
  90. break
  91. if response is None:
  92. wrapped_callback = self.make_view_atomic(callback)
  93. try:
  94. response = wrapped_callback(request, *callback_args, **callback_kwargs)
  95. except Exception as e:
  96. response = self.process_exception_by_middleware(e, request)
  97. # Complain if the view returned None (a common error).
  98. if response is None:
  99. if isinstance(callback, types.FunctionType): # FBV
  100. view_name = callback.__name__
  101. else: # CBV
  102. view_name = callback.__class__.__name__ + '.__call__'
  103. raise ValueError(
  104. "The view %s.%s didn't return an HttpResponse object. It "
  105. "returned None instead." % (callback.__module__, view_name)
  106. )
  107. # If the response supports deferred rendering, apply template
  108. # response middleware and then render the response
  109. elif hasattr(response, 'render') and callable(response.render):
  110. for middleware_method in self._template_response_middleware:
  111. response = middleware_method(request, response)
  112. # Complain if the template response middleware returned None (a common error).
  113. if response is None:
  114. raise ValueError(
  115. "%s.process_template_response didn't return an "
  116. "HttpResponse object. It returned None instead."
  117. % (middleware_method.__self__.__class__.__name__)
  118. )
  119. try:
  120. response = response.render()
  121. except Exception as e:
  122. response = self.process_exception_by_middleware(e, request)
  123. return response
  124. def process_exception_by_middleware(self, exception, request):
  125. """
  126. Pass the exception to the exception middleware. If no middleware
  127. return a response for this exception, raise it.
  128. """
  129. for middleware_method in self._exception_middleware:
  130. response = middleware_method(request, exception)
  131. if response:
  132. return response
  133. raise
  134. def reset_urlconf(sender, **kwargs):
  135. """Reset the URLconf after each request is finished."""
  136. set_urlconf(None)
  137. request_finished.connect(reset_urlconf)