专栏名称: 编程派
Python程序员都在看的公众号,跟着编程派一起学习Python,看最新国外教程和资源!
目录
相关文章推荐
Python爱好者社区  ·  DeepSeek全攻略 ... ·  3 天前  
Python爱好者社区  ·  中国最难入的IT公司。 ·  3 天前  
Python爱好者社区  ·  黄仁勋预言成真!!! ·  昨天  
Python爱好者社区  ·  DeepSeek彻底爆了。。。 ·  4 天前  
Python开发者  ·  湖南大学的 DeepSeek ... ·  2 天前  
51好读  ›  专栏  ›  编程派

Flask 源码解析:请求

编程派  · 公众号  · Python  · 2017-05-24 11:50

正文

原文: http://cizixs.com/2017/01/18/flask-insight-request

全文约 9500 字,读完可能需要 14 分钟。

这是 flask 源码解析系列文章的其中一篇,本系列已发布文章列表:

简介

对于物理链路来说,请求只是不同电压信号,它根本不知道也不需要知道请求格式和内容到底是怎样的; 对于 TCP 层来说,请求就是传输的数据(二进制的数据流),它只要发送给对应的应用程序就行了; 对于 HTTP 层的服务器来说,请求必须是符合 HTTP 协议的内容; 对于 WSGI server 来说,请求又变成了文件流,它要读取其中的内容,把 HTTP 请求包含的各种信息保存到一个字典中,调用 WSGI app; 对于 flask app 来说,请求就是一个对象,当需要某些信息的时候,只需要读取该对象的属性或者方法就行了。

可以看到,虽然是同样的请求数据,在不同的阶段和不同组件看来,是完全不同的形式。因为每个组件都有它本身的目的和功能,这和生活中的事情一个道理:对于同样的事情,不同的人或者同一个人不同人生阶段的理解是不一样的。

这篇文章呢,我们只考虑最后一个内容,flask 怎么看待请求。

请求

我们知道要访问 flask 的请求对象非常简单,只需要 from flask import request

  1. from flask import request

  2. with app.request_context(environ):

  3.    assert request.method == 'POST'

前面一篇文章 已经介绍了这个神奇的变量是怎么工作的,它最后对应了 flask . wrappers : Request 类的对象。 这个类内部的实现虽然我们还不清楚,但是我们知道它接受 WSGI server 传递过来的 environ 字典变量,并提供了很多常用的属性和方法可以使用,比如请求的 method、path、args 等。 请求还有一个不那么明显的特性——它不能被应用修改,应用只能读取请求的数据。

这个类的定义很简单,它继承了 werkzeug . wrappers : Request ,然后添加了一些属性,这些属性和 flask 的逻辑有关,比如 view_args、blueprint、json 处理等。它的代码如下:

  1. from werkzeug.wrappers import Request as RequestBase

  2. class Request(RequestBase):

  3.     """

  4.    The request object is a :class:`~werkzeug.wrappers.Request` subclass and

  5.    provides all of the attributes Werkzeug defines plus a few Flask

  6.    specific ones.

  7.    """

  8.    #: The internal URL rule that matched the request.  This can be

  9.    #: useful to inspect which methods are allowed for the URL from

  10.    #: a before/after handler (``request.url_rule.methods``) etc.

  11.    url_rule = None

  12.    #: A dict of view arguments that matched the request.  If an exception

  13.     #: happened when matching, this will be ``None``.

  14.    view_args = None

  15.    @property

  16.    def max_content_length(self):

  17.        """Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""

  18.        ctx = _request_ctx_stack.top

  19.        if ctx is not None:

  20.             return ctx.app.config['MAX_CONTENT_LENGTH']

  21.    @property

  22.    def endpoint(self):

  23.        """The endpoint that matched the request.  This in combination with

  24.        :attr:`view_args` can be used to reconstruct the same or a

  25.        modified URL.  If an exception happened when matching, this will

  26.        be ``None``.

  27.        """

  28.        if self.url_rule is not None:

  29.            return self.url_rule.endpoint

  30.    @property

  31.    def blueprint(self):

  32.         """The name of the current blueprint"""

  33.        if self.url_rule and '.' in self.url_rule.endpoint:

  34.            return self.url_rule.endpoint.rsplit('.', 1)[0]

  35.    @property

  36.     def is_json(self):

  37.        mt = self.mimetype

  38.        if mt == 'application/json':

  39.            return True

  40.        if mt.startswith('application/') and mt.endswith(' json'):

  41.             return True

  42.        return False

这段代码没有什难理解的地方,唯一需要说明的就是 @property 装饰符能够把类的方法变成属性,这是 python 中经常见到的用法。

接着我们就要看 werkzeug . wrappers : Request

  1. class Request(BaseRequest, AcceptMixin, ETagRequestMixin,

  2.               UserAgentMixin, AuthorizationMixin,

  3.              CommonRequestDescriptorsMixin):

  4.    """Full featured request object implementing the following mixins:

  5.    - :class:`AcceptMixin` for accept header parsing

  6.    - :class:`ETagRequestMixin` for etag and cache control handling

  7.    - :class:`UserAgentMixin` for user agent introspection

  8.    - :class:`AuthorizationMixin` for http auth handling

  9.    - :class:`CommonRequestDescriptorsMixin` for common headers

  10.    """

这个方法有一点比较特殊,它没有任何的 body。但是有多个基类,第一个是 BaseRequest ,其他的都是各种 Mixin 。 这里要讲一下 Mixin 机制,这是 python 多继承的一种方式,如果你希望某个类可以自行组合它的特性(比如这里的情况),或者希望某个特性用在多个类中,就可以使用 Mixin。 如果我们只需要能处理各种 Accept 头部的请求,可以这样做:

  1. class Request(BaseRequest, AcceptMixin)

  2.    pass

但是不要滥用 Mixin,在大多数情况下子类继承了父类,然后实现需要的逻辑就能满足需求。

我们先来看看 BaseRequest :

  1. class BaseRequest(object):

  2.    def __init__(self, environ, populate_request=True, shallow=False):

  3.        self.environ = environ

  4.         if populate_request and not shallow:

  5.            self.environ['werkzeug.request'] = self

  6.        self.shallow = shallow

能看到实例化需要的唯一变量是 environ ,它只是简单地把变量保存下来,并没有做进一步的处理。 Request 的内容很多,其中相当一部分是被 @cached_property 装饰的方法,比如下面这种:

  1.    @cached_property

  2.    def args(self):

  3.        """The parsed URL parameters."""

  4.        return url_decode(wsgi_get_bytes(self.environ.get('QUERY_STRING', '')),

  5.                          self







请到「今天看啥」查看全文