天天看点

werkzeug源码分析:Requestwerkzeug的Request探究

werkzeug的Request探究

werkzeug提供了Request类封装请求。位于werkzeug.wrappers模块里。

class Request(
    BaseRequest,
    AcceptMixin,
    ETagRequestMixin,
    UserAgentMixin,
    AuthorizationMixin,
    CommonRequestDescriptorsMixin,
):
    """Full featured request object implementing the following mixins:

    - :class:`AcceptMixin` for accept header parsing
    - :class:`ETagRequestMixin` for etag and cache control handling
    - :class:`UserAgentMixin` for user agent introspection
    - :class:`AuthorizationMixin` for http auth handling
    - :class:`CommonRequestDescriptorsMixin` for common headers
    """
           

请求对象有以下规则:

  • 请求对象是不可变对象,除非修改内部数据结构。
  • 请求对象是线程共享的,不是线程安全的。如果在多线程中使用清加锁。这个规则实际上是针对请求对象作为全局变量而言的,如flask,因此flask实现了request线程隔离。
  • 请求对象不能被序列化。

可以看出,这个类是通过继承其他类类实现功能的。基本功能由BaseRequest类提供(意味着如果不需要这些混入类的功能,Request类和BaseRequest类功能一样),其他功能由Mixin类提供。从继承的类可以看出,我们也可以通过继承新的Mixin类来增加功能。默认下继承了5个Mixin类。

  • AcceptMixin提供了解析请求头功能。
  • ETagResponseMixin提供了etag和cache处理功能。
  • UserAgentMixin提供了用户代理内省功能。
  • AuthorizationMixin提供了HTTP认证处理。
  • CommonRequestDescriptorsMixin提供了各种HTTP请求头字段描述器。

分析

先分析出现较多的两个描述器:

@cached_property

environ_property

@cached_property

class cached_property(property):
    def __init__(self, func, name=None, doc=None):
        self.__name__ = name or func.__name__
        self.__module__ = func.__module__
        self.__doc__ = doc or func.__doc__
        self.func = func

    def __set__(self, obj, value):
        obj.__dict__[self.__name__] = value

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        value = obj.__dict__.get(self.__name__, _missing)
        if value is _missing:
            value = self.func(obj)
            obj.__dict__[self.__name__] = value
        return value
           

这个描述器的作用是把函数转变为懒惰属性。懒惰属性就是只有当真正调用时才会执行计算且当第一次调用时会计算一次,之后直接获取结果的属性。

cached_property

类继承

property

类,重写了__init__、set、和__get__方法。如:

@cached_property
def host(self):
    return get_host(self.environ, trusted_hosts=self.trusted_hosts)
           

当第一次调用request.host时,会把request对象作为obj,request的类作为type传入__get__,因此request的字典没有host这个属性,因此执行这条语句

value = obj.__dict__.get(self.__name__, _missing)

,value得到的是_missing。然后进入if语句,真正执行函数调用,并把这个结果作为属性写到request的字典里。那么之后执行这条语句

value = obj.__dict__.get(self.__name__, _missing)

时得到的不再是_mising,而是第一次执行得到的结果,所以不会再次执行函数调用了,这也是为什么叫懒惰属性的原因。

environ_property

class environ_property(_DictAccessorProperty):
    read_only = True

    def lookup(self, obj):
        return obj.environ


class _DictAccessorProperty(object):
    """Baseclass for `environ_property` and `header_property`."""

    read_only = False

    def __init__(
        self,
        name,
        default=None,
        load_func=None,
        dump_func=None,
        read_only=None,
        doc=None,
    ):
        self.name = name
        self.default = default
        self.load_func = load_func
        self.dump_func = dump_func
        if read_only is not None:
            self.read_only = read_only
        self.__doc__ = doc

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        storage = self.lookup(obj)  # 得到的就是environ
        if self.name not in storage:
            return self.default
        rv = storage[self.name]
        if self.load_func is not None:  # load_func可以对属性进一步处理
            try:
                rv = self.load_func(rv)
            except (ValueError, TypeError):
                rv = self.default
        return rv

    def __set__(self, obj, value):
        if self.read_only:
            raise AttributeError("read only property")
        if self.dump_func is not None:
            value = self.dump_func(value)
        self.lookup(obj)[self.name] = value

    def __delete__(self, obj):
        if self.read_only:
            raise AttributeError("read only property")
        self.lookup(obj).pop(self.name, None)

    def __repr__(self):
        return "<%s %s>" % (self.__class__.__name__, self.name)
           

将请求属性映射到环境变量。实际上就是从environ中获取所需的属性。如查询字符串,直接把"QUERY_STRING"作为参数传给environ_property即可,它把从environ映射查询字符串属性。environ不一定是请求环境,只要提供了environ这个属性即可。如果需要映射的属性不存在,返回default(默认为None)。这些映射的属性都是read-only,除非指定read_only为False。

BaseRequest类就是对environ进一步封装,提供更友好的接口处理请求。如:

  • 提供headers懒惰属性,这个属性可以获取所有有关请求头的信息。
  • 提供cookies懒惰属性,这个属性解析了请求的cookie。
  • 提供files懒惰属性,这个属性获取上传的二进制文件。
  • 提供form懒惰属性,这个属性解析了请求表格数据。
  • 提供了args懒惰属性,这个属性解析了查询参数。

headers

@cached_property
def headers(self):
     return EnvironHeaders(self.environ)
           

EnvironHeaders实际上还是Headers类,只是限制了headers是只读的。Headers提供了接口和dict基本相同。EnvironHeaders重写了Headers的__getitem__方法,最核心的地方也是这个方法:

def __getitem__(self, key, _get_mode=False):
    if not isinstance(key, string_types):
        raise KeyError(key)
    key = key.upper().replace("-", "_")
    if key in ("CONTENT_TYPE", "CONTENT_LENGTH"):
        return _unicodify_header_value(self.environ[key])
    return _unicodify_header_value(self.environ["HTTP_" + key])
           

__getitem__就是把environ的请求头部数据提取出来。

cookies

@cached_property
def cookies(self):
    return parse_cookie(
        self.environ,
        self.charset,
        self.encoding_errors,
        cls=self.dict_storage_class,
    )
           

实际上就是解析environ中的cookie请求头部字段然后存储到

self.dict_storage_class

中。

其他的files、form等处理和上面类似,可自行了解。