Django HTTP处理流程(自我总结)
Django中由wsgi模块接管http请求,核心处理方法为get_wsgi_application,其定义如下:
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()
WSGIHandler(代码有节减):
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) 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) response._handler_class = self.__class__ status = '%s %s' % (response.status_code, response.reason_phrase) response_headers = [(str(k), str(v)) for k, v in response.items()] for c in response.cookies.values(): response_headers.append((str('Set-Cookie'), str(c.output(header='')))) start_response(force_str(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对象被调用的时候,首先会判断请求中间件是否为None,如果为None,则通过self.load_middleware()加载中间件。之后调用self.get_response(request)获得http响应对象。BaseHandler的load_middleware、get_response方法定义如下:
BaseHandler(代码有节减):
class BaseHandler(object): # Changes that are always applied to a response (in this order). response_fixes = [ 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 load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE_CLASSES. Must be called after the environment is fixed (see __call__ in subclasses). """ self._view_middleware = [] self._template_response_middleware = [] self._response_middleware = [] self._exception_middleware = [] request_middleware = [] for middleware_path in settings.MIDDLEWARE_CLASSES: mw_class = import_string(middleware_path) try: mw_instance = mw_class() except MiddlewareNotUsed as exc: if settings.DEBUG: if six.text_type(exc): logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc) else: logger.debug('MiddlewareNotUsed: %r', middleware_path) continue if hasattr(mw_instance, 'process_request'): request_middleware.append(mw_instance.process_request) if hasattr(mw_instance, 'process_view'): self._view_middleware.append(mw_instance.process_view) if hasattr(mw_instance, 'process_template_response'): self._template_response_middleware.insert(0, mw_instance.process_template_response) if hasattr(mw_instance, 'process_response'): self._response_middleware.insert(0, mw_instance.process_response) if hasattr(mw_instance, 'process_exception'): self._exception_middleware.insert(0, mw_instance.process_exception) # We only assign to this when initialization is complete as it is used # as a flag for initialization being complete. self._request_middleware = request_middleware 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.get_resolver(urlconf) # Use a flag to check if the response was rendered to prevent # multiple renderings or to force rendering if necessary. response_is_rendered = False try: response = None # Apply request middleware for middleware_method in self._request_middleware: 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.get_resolver(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: response = middleware_method(request, callback, callback_args, callback_kwargs) if response: break 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 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__)) try: response = response.render() except Exception as e: response = self.process_exception_by_middleware(e, request) response_is_rendered = True except http.Http404 as exc: logger.warning('Not Found: %s', request.path, extra={ 'status_code': 404, 'request': request }) if settings.DEBUG: response = debug.technical_404_response(request, exc) else: response = self.get_exception_response(request, resolver, 404, exc) try: # Apply response middleware, regardless of the response for middleware_method in self._response_middleware: 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) # If the exception handler returns a TemplateResponse that has not # been rendered, force it to be rendered. if not response_is_rendered and callable(getattr(response, 'render', None)): response = response.render() return response
get_response方法中,循环遍历执行已经加载的request middleware,调用middleware将返回None 或者HttpResponse对象,如果返回前者,继续处理其它中间件,如果返回一个 HttpResponse,就处理中止。接着,应用view middleware,通过 url resolve匹配路由获取view functions(如果使用的通用视图,通过调用as_view方法,此方法为闭包,返回一个view方法)。如果middleware支持渲染(render),则应用template response中间件,然后通过调用HttpResponse对象的render方法渲染模板响应输出。