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
"""
请求对象有以下规则:
可以看出,这个类是通过继承其他类类实现功能的。基本功能由BaseRequest类提供(意味着如果不需要这些混入类的功能,Request类和BaseRequest类功能一样),其他功能由Mixin类提供。从继承的类可以看出,我们也可以通过继承新的Mixin类来增加功能。默认下继承了5个Mixin类。
先分析出现较多的两个描述器:@cached_property
和environ_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,而是第一次执行得到的结果,所以不会再次执行函数调用了,这也是为什么叫懒惰属性的原因。
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进一步封装,提供更友好的接口处理请求。如:
@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的请求头部数据提取出来。
@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等处理和上面类似,可自行了解。