Django的一次请求到响应的流程
admin
2023-07-31 00:44:44
0

Django的请求到响应的处理流程本质上差不多,简单来说,都是利用WSGI,针对request,进行response。当然在响应前会发送request_started信号,会调用预处理函数(在Flask中是before_request,Django是请求中间件,process_request),响应完成后会发送request_finished函数,调用响应后函数(在Flask中是after_request中,Django中是process_response)。思想差不多,但是处理细节上还是有很多不同的。

  1. 用户浏览器一个url,发起一个请求

在Web应用启动后,会生成一个 WSGIHandler 实例(根据setting中的WSGI_APPLICATION = ‘dailyblog.wsgi.application’ 调用函数),每次请求响应都用这个实例。 settings 里设置了 WSGI application。 返回实例代码:

1234567891011 from django.core.handlers.wsgi import WSGIHandlerdef 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继承自BaseHander。这个处理器会导入项目中的 settings模块、导入 自定义例外类,最后调用自己的 load_middleware 方法,加载所有列在 MIDDLEWARE_CLASSES 中的 middleware 类并且内省它们。

BaseHander部分源代码:

123456789101112 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

请注意,只有第一次请求时会调用 load_middleware,以后不再调用了。
其中,request_middleware、view_middleware 是顺序,template_response_middleware、response_middleware、exception_middleware 是逆序。

一 个 middleware 类可以包括请求响应过程的四个阶段:request,view,response 和 exception。对应的方法:process_request,process_view, process_response 和 process_exception。我们在middleware中间件中定义其中的方法。

3、构造WSGIRequest。
WSGIHandler 处理器准备工作已经完成,随后它给调度程序发送一个信号 request_started(这个和Flask中的request_started信号差不多),然后根据入 environ 构造 WSGIRequest 对象,它的父类是HttpRequest。

4、 处理Middleware的request中间件
WSGIHander的get_response方法处理 _request_middleware 实例变量并调用其中的每一个方法,传入 HttpRequest 的实例作为参数,即请求到达Request Middlewares,中间件对request做一些预处理,如果中间件返回response,会直接响应请求。

5、 URLConf通过urls.py文件和请求的URL找到相应的视图函数

此时会创建django.core.urlresolvers.RegexURLResolver 的一个实例。

URLresolver 遵循一个相当简单的模式。对于在 URL 配置文件中根据 ROOT_URLCONF 的配置产生的每一个在 urlpatterns 列表中的条目,它会检查请 求的 URL 路径是否与这个条目的正则表达式相匹配,如果是的话,有两种选择:

  1. 如果这个条目有一个可以调用的 include,resolver 截取匹配的 URL,转 到 include 指定的 URL 配置文件并开始遍历其中 urlpatterns 列表中的 每一个条目。根据你 URL 的深度和模块性,这可能重复好几次。
  2. 否则,resolver 返回三个条目:匹配的条目指定的 view function;一个 从 URL 得到的未命名匹配组(被用来作为 view 的位置参数);一个关键 字参数字典,它由从 URL 得到的任意命名匹配组和从 URLConf 中得到的任 意其它关键字参数组合而成。

注意这一过程会在匹配到第一个指定了 view 的条目时停止,因此最好让你的 URL 配置从复杂的正则过渡到简单的正则,这样能确保 resolver 不会首先匹配 到简单的那一个而返回错误的 view function。

如果没有找到匹配的条目,resolver 会产生 django.core.urlresolvers.Resolver404 异常,它是 django.http.Http404 例 外的子类。后面我们会知道它是如何处理的。

6、 开始调用View中相应的视图函数或基于类的视图。

7、View进行一些处理,如通过模型Models返回数据。

8、如果需要,Views可以创建一个额外的Context,Context被当做变量传给Template。

9、Template渲染输出

10、渲染后的输出被返回到View

11、HTTPResponse被发送到Response Middlewares

12、Response Middlewares对response进行特定的处理,然后返回一个新的response

13、Response返回呈现给用户

14、一旦 middleware完成了最后环节,处理器将发送一个信号 request_finished,订阅这个信号的事件会清空并释放任何使用中的资源。比如,Django 的 request_finished 的订阅者subscriber会关闭所有数据库连接。

15、所有流程至此已经全部完成。

下面是处理器的源代码:

12345678910111213141516171819202122232425262728293031323334353637383940414243 class WSGIHandler(base.BaseHandler):    initLock = Lock()    request_class = WSGIRequest #创建 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 respons

异常情况:

如果 view 函数,或者其中的什么东西,发生了异常,那么 get_response将遍历它的 _exception_middleware 实例变量并呼叫那里的每个方法,传入 HttpResponse 和这个 exception 作为参数。如果顺利,最后会实例化,并返回一个 HttpResponse 。

这时候有可能还是没有得到一个 HttpResponse,这可能有几个原因:

  1. view 并没有返回内容。
  2. view 可能抛出了异常,但middleware没有 能处理它。
  3. 一个 middleware 方法试图处理一个异常时,自己又产生了一个异常。

处理器会做些相应的异常处理,如404,403,500等等,具体可以看上面的源代码。

里面有些地方是参考其他博客文章,但忘了地址了。如有原作者看到,请给予提示,谢谢。

相关内容

热门资讯

Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
项目管理和工程管理的区别 项目管理 项目管理,顾名思义就是专注于开发和完成项目的管理,以实现目标并满足成功标准和项目要求。 工...