1.怎么创建一个server
2.怎么处理请求
3.怎么处理响应
创建一个server
一般都是通过run方法来创建一个server的
def run(self, host='localhost', port=5000, **options):
from werkzeug import run_simple
if 'debug' in options:
self.debug = options.pop('debug')
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
return run_simple(host, port, self, **options)
这里用的是werkzeug的run_simple(这里是werkzeug 0.1版本)
def run_simple(hostname, port, application, use_reloader=False,
extra_files=None, threaded=False, processes=1):
def inner():
srv = make_server(hostname, port, application, threaded,
processes)
try:
srv.serve_forever()
except KeyboardInterrupt:
pass
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
print '* Running on http://%s:%d/' % (hostname or '0.0.0.0', port)
if use_reloader:
test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
test_socket.bind((hostname, port))
test_socket.close()
run_with_reloader(inner, extra_files or [])
else:
inner()
这里主要看是否是debug模式,如果是要先将建立一下socket连接,如果不是就直接通过make_server()建立一个wsgi server
如果你启用了调试支持,服务器会在代码修改后自动重新载入,并在发生错误时提供一个相当有用的调试器。
Request定义
from werkzeug import Request as RequestBase
class Request(RequestBase):
def __init__(self, environ):
RequestBase.__init__(self, environ)
self.endpoint = None
self.view_args = None
在这里可以看到它是引用的werkzeug的Request作为父类
class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
UserAgentMixin, AuthorizationMixin,
CommonRequestDescriptorsMixin):
class BaseRequest(object):
def __init__(self, environ, populate_request=True, shallow=False):
self.environ = environ
if populate_request and not shallow:
self.environ['werkzeug.request'] = self
print('wsgi environ:',environ)
self.shallow = shallow
def __repr__(self):
args = []
try:
args.append("'%s'" % self.url)
args.append('[%s]' % self.method)
except:
args.append('(invalid WSGI environ)')
return '<%s %s>' % (
self.__class__.__name__,
' '.join(args)
)
打印了一下environ看看它是什么样子
('wsgi environ:', ['wsgi.multiprocess', 'SERVER_SOFTWARE', 'SCRIPT_NAME', 'REQUEST_METHOD', 'PATH_INFO', 'SERVER_PROTOCOL', 'QUERY_STRING', 'CONTENT_LENGTH', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'SERVER_NAME', 'REMOTE_PORT', 'wsgi.url_scheme', 'SERVER_PORT', 'werkzeug.request', 'wsgi.input', 'HTTP_HOST', 'wsgi.multithread', 'HTTP_UPGRADE_INSECURE_REQUESTS', 'HTTP_CACHE_CONTROL', 'HTTP_ACCEPT', 'wsgi.version', 'wsgi.run_once', 'wsgi.errors', 'REMOTE_ADDR', 'HTTP_ACCEPT_LANGUAGE', 'CONTENT_TYPE', 'HTTP_ACCEPT_ENCODING'])
其中werkzeug.request是这个样子的
'werkzeug.request': <Request 'http://localhost:5000/' [GET]>
解释一下继承的这几个类的作用
- :class:AcceptMixin
:主要用来获取http头的信息
举个例子
from werkzeug.http import parse_accept_header
from werkzeug.datastructures import MIMEAccept
from werkzeug.utils import cached_property
class AcceptMixin(object):
@cached_property
def accept_mimetypes(self):
return parse_accept_header(self.environ.get('HTTP_ACCEPT'), MIMEAccept)
parse_accept_header方法这样的
def parse_accept_header(value, cls=None):
if cls is None:
cls = Accept
if not value:
return cls(None)
result = []
for match in _accept_re.finditer(value):
quality = match.group(2)
if not quality:
quality = 1
else:
quality = max(min(float(quality), 1), 0)
result.append((match.group(1), quality))
return cls(result)
可以从 parse_accept_header()的源码看出它接受的是一个class
这里的MIMEAccept是一个class
class MIMEAccept(Accept):
"""Like :class:`Accept` but with special methods and behavior for
mimetypes.
"""
def _value_matches(self, value, item):
def _normalize(x):
x = x.lower()
return x == '*' and ('*', '*') or x.split('/', 1)
# this is from the application which is trusted. to avoid developer
# frustration we actually check these for valid values
if '/' not in value:
raise ValueError('invalid mimetype %r' % value)
value_type, value_subtype = _normalize(value)
if value_type == '*' and value_subtype != '*':
raise ValueError('invalid mimetype %r' % value)
if '/' not in item:
return False
item_type, item_subtype = _normalize(item)
if item_type == '*' and item_subtype != '*':
return False
return (
(item_type == item_subtype == '*' or
value_type == value_subtype == '*') or
(item_type == value_type and (item_subtype == '*' or
value_subtype == '*' or
item_subtype == value_subtype))
)
@property
def accept_html(self):
"""True if this object accepts HTML."""
return (
'text/html' in self or
'application/xhtml+xml' in self or
self.accept_xhtml
)
@property
def accept_xhtml(self):
"""True if this object accepts XHTML."""
return (
'application/xhtml+xml' in self or
'application/xml' in self
)
这里还用了装饰器@cached_property
class cached_property(object):
def __init__(self, func, name=None, doc=None, writeable=False):
if writeable:
from warnings import warn
warn(DeprecationWarning('the writeable argument to the '
'cached property is a noop since 0.6 '
'because the property is writeable '
'by default for performance reasons'))
self.__name__ = name or func.__name__
self.__module__ = func.__module__
self.__doc__ = doc or func.__doc__
self.func = func
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
我发现很多源码上都爱用描述符,这里来对描述符做一个简单的解释
简单来讲,描述符就是一个Python对象,但这个对象比较特殊,特殊性在于其属性的访问方式不再像普通对象那样访问,它通过一种叫描述符协议的方法来访问。这些方法包括get、set、delete。定义了其中任意一个方法的对象都叫描述符。
- :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