django从请求到响应的过程

栏目: Python · 发布时间: 6年前

内容简介:django从请求到响应的过程

一直都想写一篇这样的文章,不过一直不知道怎么下手,在掘金上看到这样一篇文章,觉得写得挺好,然后结合自己的一些理解,便写下这篇博客,但大部分的内容以及逻辑都来自原作者,所以属于转载内容。

原文传送门: juejin.im/post/5a6951…

WSGI

WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,也不用于与程序交互的API,更不是代码,而只是定义了一个接口,用于描述web server如何与web application通信的规范。 当客户端发送一次请求后,最先处理请求的实际上是 web 服务器就是我们经常说的 nginx、Apache 这类的 web 服务器,然后web服务器再把请求交给web应用程序(如django)处理,这中间的中介就是WSGI,它把 web 服务器和 web 框架 (Django) 连接起来。

django从请求到响应的过程

简单介绍一下WSGI的一些内容,它规定应用是可调用对象(函数/方法),然后它接受2个固定参数:一个是含有服务器端的环境变量,另一个是可调用对象,这个对象用来初始化响应,给响应加上status code状态码和httpt头部,并且返回一个可调用对象。可以看个简单的例子

# 这段代码来自 python 核心编程
def simplr_wsgi_app(environ, start_response):
	# 固定两个参数,django中也使用同样的变量名
	status = '200 OK'
	headers = [{'Content-type': 'text/plain'}]
	# 初始化响应, 必须在返回前调用
	start_response(status, headers)
	# 返回可迭代对象
	return ['hello world!']

django中,实现同样逻辑的是通过WSGIHandler这个类,下面我们也会重点介绍它! 如果对WSGI与uWSGI有兴趣的,推荐大家看这篇文章,WSGI & uwsgi ,大赞!

中间件基本概念

顾名思义,中间件是位于Web服务器端和Web应用之间的,它可以添加额外的功能。当我们创建一个django项目(通过pycharm),它会自动帮我们设置一些必要的中间件。

MIDDLEWARE_CLASSES = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

中间件要么对来自用户的数据进行预处理,然后发送给应用;要么在应用将响应负载返回给用户之前,对结果数据进行一些最终的调整。通俗一点,在django中,中间能够帮我们准备好request这个对象,然后应用可以直接使用request对象获取到各类数据,也帮我们将response添加头部,状态码等。

数据流

当django接受到一个请求时,会初始化一个WSGIHandler,可以在项目下的wsgi.py文件进行跟踪,你就会发现这一个类。

class WSGIHandler(base.BaseHandler):
    def __call__(self, environ, start_response):
	    pass

这个类遵循WSGI应用的规定,它接受2个参数:一个是含有服务器端的环境变量,另一个是可调用对象,返回一个可迭代对象。 这个handler控制了从请求到响应的整个过程,主要流程:

django从请求到响应的过程

中间类中的顺序与方法

django 的中间件类至少含有以下四个方法中的一个:

process_request、 process_view、process_exception、process_response

WSGIHandler通过load_middleware将这个些方法分别添加到_request_middleware、_view_middleware、_response_middleware 和 _exception_middleware四个列表中。

并不是每个中间件都有这4个方法,如果不存在某个方法,那么在加载的过程中,这个类就被跳过。

for middleware_path in settings.MIDDLEWARE_CLASSES:
	···
    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)

我们可以从源码看出,process request 和 process response的执行加载顺序正好是相反,在循环中,process_request是被append到列表的末尾,而process_request是被insert到最前面的。

django从请求到响应的过程

(可能有些情况Comment中间件在Session前面,了解加载的顺序就好了)

process_request

举几个中间件的例子

class CommonMiddleware(object):
# 伪代码
    def process_request(self, request):

        # Check for denied User-Agents
        if 'HTTP_USER_AGENT' in request.META:
            for user_agent_regex in settings.DISALLOWED_USER_AGENTS:
                if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
                    raise PermissionDenied('Forbidden user agent')
        host = request.get_host()

        if settings.PREPEND_WWW and host and not host.startswith('www.'):
            host = 'www.' + host
		pass

CommonMiddleware的process_request主要是判断用户代理是否符合要求以及在完善URL,如增加www或者末尾加/。

class SessionMiddleware(object):
    def process_request(self, request):
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        request.session = self.SessionStore(session_key)

SessionMiddleware的process_request是把session_key从cookies中取出来然后放到request.session中。

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))

在前面提过,中间件的加载是按照一定顺序(正反序), AuthenticationMiddleware的process_request方法基于session中间件被加载过了,然后才将用户取出来放入到 request.user 。

process_request 应该返回 None 或者 HTTPResponse 对象。当返回 None 时,WSGI handler 会继续加载 process_request 里面的方法,如果是后一种情况,那么Handlers会跳过遍历后面的类的process_request方法,直接进入进入下一个步骤。

解析 url

当_request_middleware列表中的 process_request 被遍历完,会得到一个经过处理的request对象(加入了request.session,request.user等属性)。

django将按顺序进行对url进行正则匹配,如果匹配不成功,就会抛出异常 django.core.urlresolvers.Resolver404。

process_view

经过url的匹配,会获得视图函数以及相关参数。在调用view函数之前,django会先加载_view_middleware中的各个process_view方法。

逐个默认的中间件看了一遍,只看到csrf有这个方法

# 伪代码
class CsrfViewMiddleware(object):

    def process_view(self, request, callback, callback_args, callback_kwargs):

        if getattr(request, 'csrf_processing_done', False):
            return None

        try:
            csrf_token = _sanitize_token(
                request.COOKIES[settings.CSRF_COOKIE_NAME])
            # Use same token next time
            request.META['CSRF_COOKIE'] = csrf_token
        except KeyError:
            csrf_token = None
        if getattr(callback, 'csrf_exempt', False):
            return None
        pass

这个方法的作用是判断cookiers中是否存在csrf的字段,如果不存在,会直接抛出异常,如果存在,返回None。

执行view逻辑

view函数需要满足:

  1. 基于函数(FBV)或者基于类的(CVB)的视图。
  2. 接受的参数第一个必须为request,并且需要返回一个response对象。

如果视图函数抛出一个异常,Handler 将会循环遍历_exception_middleware 列表,如果有一个异常被抛出,后面的 process_exception 将不会被执行。

process_response

在这个阶段,我们得到了一个 HTTPResponse 对象,这个对象可能是 process_view 返回的,也可能是视图函数返回的。现在我们将循环访问响应中间件。这是中间件调整数据的最后的机会。举个例子:

class XFrameOptionsMiddleware(object):

    def process_response(self, request, response):
        # Don't set it if it's already in the response
        if response.get('X-Frame-Options') is not None:
            return response

        # Don't set it if they used @xframe_options_exempt
        if getattr(response, 'xframe_options_exempt', False):
            return response

        response['X-Frame-Options'] = self.get_xframe_options_value(request,
                                                                    response)
        return response

XFrameOptionsMiddleware将X-Frame-Options加入到response当中,防止网站被嵌套、被劫持。

class CsrfViewMiddleware(object):
    def process_response(self, request, response):
        if getattr(response, 'csrf_processing_done', False):
            return response

        if not request.META.get("CSRF_COOKIE_USED", False):
            return response

        # Set the CSRF cookie even if it's already set, so we renew
        # the expiry timer.
        response.set_cookie(settings.CSRF_COOKIE_NAME,
                            request.META["CSRF_COOKIE"],
                            max_age=settings.CSRF_COOKIE_AGE,
                            domain=settings.CSRF_COOKIE_DOMAIN,
                            path=settings.CSRF_COOKIE_PATH,
                            secure=settings.CSRF_COOKIE_SECURE,
                            httponly=settings.CSRF_COOKIE_HTTPONLY
                            )
        # Content varies with the CSRF cookie, so set the Vary header.
        patch_vary_headers(response, ('Cookie',))
        response.csrf_processing_done = True
        return response

CsrfViewMiddleware在response中设置csrf cookies

最后

当response的中间件加载完,系统在返回之前会调用WSGI服务器端传过来的start_response方法对象,初始化响应,然后进行response响应。

总结

本文重点在于:

  1. 理解WSGI协议,并且WSGIHandler这个类控制整个请求到响应的流程,以及整个流程的基本过程。
  2. 中间件的概念,以及每一个process_request, process_response, process_view, process_exception方法在哪个步骤发挥着什么样的作用。
  3. 中间价的执行时有顺序的,request与view是按照顺序去执行的,而response和exception是反序的,这一步实在WSGIHandler在加载到它的各个列表的时候完成的。

以上所述就是小编给大家介绍的《django从请求到响应的过程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

分布式服务架构:原理、设计与实战

分布式服务架构:原理、设计与实战

李艳鹏、杨彪 / 电子工业出版社 / 2017-8 / 89.00

《分布式服务架构:原理、设计与实战》全面介绍了分布式服务架构的原理与设计,并结合作者在实施微服务架构过程中的实践经验,总结了保障线上服务健康、可靠的最佳方案,是一本架构级、实战型的重量级著作。 《分布式服务架构:原理、设计与实战》以分布式服务架构的设计与实现为主线,由浅入深地介绍了分布式服务架构的方方面面,主要包括理论和实践两部分。理论上,首先介绍了服务架构的背景,以及从服务化架构到微服务架......一起来看看 《分布式服务架构:原理、设计与实战》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具