Django的中间件以及Request/Response循环
前言:萌新的疑问
在建立一个新的Django项目时,你要做的第一件事就是连接你的URLconf并设置一些视图。 但是这里真的发生了什么? Django如何将流量路由到视图,中间件在这个周期中扮演什么角色?
我以前眼中的Django:
WSGI
WSGI是为解决一个基本问题而创建的工具:将Web服务器连接到Web框架。 WSGI有两个方面:'服务器'方面和'应用程序'方面。 为了处理WSGI响应,服务器执行应用程序,并向应用程序端提供回调函数。 应用程序处理请求并使用提供的回调将响应返回给服务器。 本质上,WSGI处理程序充当您的Web服务器(Apache,NGINX等)和您的Django项目之间的守门员。
服务器和应用程序之间是中间件。 您可以将中间件视为一系列双向过滤器:它们可以改变(或短路)在网络和Django应用程序之间来回传输的数据。
整个Django的执行过程:数据流
当用户请求你的应用程序时,WSGI处理程序被实例化,然后Django按照顺序处理以下环节:
- 导入你的settings.py文件和Django的异常类。
- 加载它在位于settings.py中的MIDDLEWARE_CLASSES或者。MIDDLEWARES(取决于Django版本)元组中找到的所有中间件类。
- 构建处理 request,view, response和exception的四个方法列表。
- 通过请求方法循环,按顺序运行它们。
- 解决了所请求的URL。
- 循环每个视图处理方法。
- 调用视图函数(通常是渲染模板)。
- 处理任何异常方法。
- 循环遍历每个响应方法(从内向外,从请求中间件的相反顺序)。
- 最后建立一个返回值并调用web服务器的回调函数。
中间件
中间件被用在Django项目中的许多关键功能中:例如,使用CSRF中间件来防止跨站请求伪造攻击。 他们用来处理会话数据。 身份验证和授权是使用中间件完成的。 您可以编写自己的中间件类来通过应用程序调整(或短路)数据流。
process_request
Django中间件必须至少包含以下方法之一:process_request,process_response,process_view 和 process_exception。 这些是由WSGI处理程序收集的方法,然后按列出的顺序调用。 让我们快速浏览django.contrib.auth.middleware.AuthenticationMiddleware,这是运行django-admin.py startproject时默认安装的中间件之一:
def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request)
return request._cached_user
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request))
正如你所看到的,这个中间件只能处理来自Django应用程序的数据流的“请求”步骤。 该中间件首先验证会话中间件是否已经被使用,然后通过调用get_user帮助函数来设置用户。 当WSGI处理程序迭代process_request方法列表时,它会构建这个请求对象,最终将被传递到视图中,并且您将能够引用request.user。 settings.py中有一些中间件没有有process_request方法。 不过没关系,在这个阶段刚刚被跳过。
process_request应该返回None(如本例),或者可以返回一个HttpResponse对象。 在前一种情况下,WSGI处理程序将继续处理process_request方法,后者将“短路”进程并开始process_response循环。【如上图所示,跳过view过程,直接去response】
Resolve the URL(解析URL)
现在process_request方法已经被调用了,现在我们有一个请求对象将被传递给视图。在此之前,Django必须解析URL并确定调用哪个视图函数。这只是通过正则表达式匹配来完成的。您的settings.py将会有一个名为ROOT_URLCONF的键,它表示“root”urls.py文件,您将从中为每个应用程序添加urls.py文件。 URL路由在Django教程中已经非常广泛地介绍了,所以在这里就不需要了。
一个观点有三个要求:
- 它必须是可调用的。它可以是基于函数的视图,也可以是继承自基于类的视图。根据HTTP动作(GET,POST等)查看as_view() 方法以使其可调用。
- 它必须接受一个HttpRequest对象作为第一个位置参数。这是调用所有process_request和process_view中间件方法的结果。
- 它必须返回一个HttpResponse对象,或引发一个异常。这是用于启动WSGI处理程序的process_view循环的响应对象。
process_view
现在WSGI处理程序知道要调用哪个视图函数,它再次遍历其中间件方法列表。 任何Django中间件的process_view方法都是这样声明的:
process_view(request,view_function,view_args,view_kwargs)
和process_request一样,process_view函数必须返回None或HttpResponse对象(或引发异常),从而允许WSGI处理程序继续处理视图或“短路”并返回响应。 查看CSRF中间件的源代码,查看process_view的实例。如果存在CSRF cookie,那么process_view方法将返回None并执行视图。 如果不是,请求被拒绝,并且进程被短路,导致失败消息。
process_exception
如果view函数产生一个异常,Handler将遍历它的process_exception方法列表。 这些方法以相反的顺序执行,从settings.py中列出的最后一个中间件到第一个。 如果发生异常,则进程将短路,并且不会调用其他进程中断件。 通常我们依靠Django的BaseHandler提供的异常处理程序,但是在编写自己的定制中间件类时,您当然可以实现自己的异常处理。
process_response
在这一点上,我们将有一个HttpResponse对象,可以是由视图或由WSGI处理程序构建的process_view方法列表返回的,也可以轮流循环访问响应中间件。这是任何中间件都必须修改数据的最后机会,并且是从内层向外执行的(想象一下洋葱,视图在中心)。看一下缓存中间件源代码中的process_response实例:取决于应用程序中的不同条件(即缓存是否关闭,如果我们正在处理流等),我们需要响应存储在缓存中还是不存在。
注意:在1.10之前的Django和更高版本之间的一个区别是:在旧式MIDDLEWARE_CLASSES中,即使较早的中间件使进程短路,每个中间件也将始终调用其process_response方法。在新的MIDDLEWARES风格中,只有中间件和之前执行的中间件才会调用其process_response方法。有关MIDDLEWARES和MIDDLEWARE_CLASSES之间差异的更多详细信息,请参阅文档。
好了~
最后,Django的WSGI Handler从HttpResponse对象构建一个返回值,并执行回调函数将该数据发送到Web服务器并发送给用户。
所以,两个关键要点:
- 现在我们知道view函数是如何与URLconf相匹配的,以及实际调用的是什么(WSGI Handler)。
- 有四个关键点可以通过您自己的定制中间件进入请求/响应循环:process_request,process_response,process_view和process_exception。 想一想:请求中间件是从外部执行的,在中心点击查看,然后通过响应中间件返回到表面。