Flask---werkzeug请求、响应源码分析

示例

from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

#方式一
application1 = Response('Hello World application1!')

#方式二
def application2(environ, start_response):
    request = Request(environ)
    response = Response("Hello %s!" % request.args.get('name', 'World!'))
    return response(environ, start_response)

#方式三
@Request.application
def hello(request):
    return Response('Hello World Request!')


if __name__ == '__main__':
    # run_simple('localhost', 4000, application1)

    # run_simple('localhost', 4000, application2)
    run_simple('localhost', 4000, hello)

我们在浏览器输入http://localhost:4000/就会得到response信息
接下来我们就简单的分析下,该模块的请求、响应流程

源码分析

我们首先werkzeug包下的__init__.py模块,看看初始化做了什么操作

all_by_module = {
    'werkzeug.serving': ['run_simple'],
    'werkzeug.wsgi': ['get_current_url', 'get_host', 'pop_path_info',
                      'peek_path_info', 'SharedDataMiddleware',
                      'DispatcherMiddleware', 'ClosingIterator', 'FileWrapper',
                      'make_line_iter', 'LimitedStream', 'responder',
                      'wrap_file', 'extract_path_info'],
    'werkzeug.http': ['parse_etags', 'parse_date', 'http_date', 'cookie_date',
                      'parse_cache_control_header', 'is_resource_modified',
                      'parse_accept_header', 'parse_set_header', 'quote_etag',
                      'unquote_etag', 'generate_etag', 'dump_header',
                      'parse_list_header', 'parse_dict_header',
                      'parse_authorization_header',
                      'parse_www_authenticate_header', 'remove_entity_headers',
                      'is_entity_header', 'remove_hop_by_hop_headers',
                      'parse_options_header', 'dump_options_header',
                      'is_hop_by_hop_header', 'unquote_header_value',
                      'quote_header_value', 'HTTP_STATUS_CODES'],
    'werkzeug.wrappers': ['BaseResponse', 'BaseRequest', 'Request', 'Response',
                          'AcceptMixin', 'ETagRequestMixin',
                          'ETagResponseMixin', 'ResponseStreamMixin',
                          'CommonResponseDescriptorsMixin', 'UserAgentMixin',
                          'AuthorizationMixin', 'WWWAuthenticateMixin',
                          'CommonRequestDescriptorsMixin'],
    'werkzeug.security': ['generate_password_hash', 'check_password_hash'],
    # the undocumented easteregg ;-)
    'werkzeug._internal': ['_easteregg']
}
object_origins = {}
for module, items in iteritems(all_by_module):
    for item in items:
        object_origins[item] = module

ps:all_by_module 字典数据我删除了一部分
通过上述遍历循环字典,重新构造object_origins字典数据格式,该字典类型格式如下,我列举出来一些元素,以下是键值对形式

# BaseResponse - --- werkzeug.wrappers
# BaseRequest - --- werkzeug.wrappers
# Request - --- werkzeug.wrappers
# Response - --- werkzeug.wrappers

该字典的键是werkzeug下的某模块中的函数、方法,值是werkzeug下的某模块中

我们回头看我们的demo示例,在文件起始处我们引入了from werkzeug.serving import run_simple
我们跟踪代码去看下serving.py模块下的run_simple函数

def run_simple(hostname, port, application, use_reloader=False,
               use_debugger=False, use_evalex=True,
               extra_files=None, reloader_interval=1,
               reloader_type='auto', threaded=False,
               processes=1, request_handler=None, static_files=None,
               passthrough_errors=False, ssl_context=None)

简单说下参数的意思

hostname:应用程序的主机
port:端口
application:WSGI应用程序
use_reloader:如果程序代码修改,是否需要自动启动服务
use_debugger:程序是否要使用工具和调试系统
use_evalex:应用是否开启异常评估
extra_files:重载器应该查看的文件列表附加到模块。例如配置文件夹
reloader_interval:秒重载器的间隔
reloader_type:重载器的类型
threaded:进程是否处理单线程的每次请求
processes:如果大于1,则在新进程中处理每个请求。达到这个最大并发进程数
request_handler:可以自定义替换BaseHTTPRequestHandler
static_files:静态文件路径的列表或DICT
passthrough_errors:将此设置为“真”以禁用错误捕获。这意味着服务器会因错误而死亡
ssl_context:如何进行传输数据加密,可以设置的环境

use_reloader我们按false来举例,run_simple函数中,通过if use_reloader判断,会执行inner()方法,

 def inner():
        try:
            fd = int(os.environ['WERKZEUG_SERVER_FD'])
        except (LookupError, ValueError):
            fd = None
        srv = make_server(hostname, port, application, threaded,
                          processes, request_handler,
                          passthrough_errors, ssl_context,
                          fd=fd)
        if fd is None:
            log_startup(srv.socket)
        srv.serve_forever()

通过make_server方法,跟进我们在初始化__init__中的参数,去构造server服务

def make_server(host=None, port=None, app=None, threaded=False, processes=1,
                request_handler=None, passthrough_errors=False,
                ssl_context=None, fd=None):
    """Create a new server instance that is either threaded, or forks
    or just processes one request after another.
    """
    if threaded and processes > 1:
        raise ValueError("cannot have a multithreaded and "
                         "multi process server.")
    elif threaded:
        return ThreadedWSGIServer(host, port, app, request_handler,
                                  passthrough_errors, ssl_context, fd=fd)
    elif processes > 1:
        return ForkingWSGIServer(host, port, app, processes, request_handler,
                                 passthrough_errors, ssl_context, fd=fd)
    else:
        return BaseWSGIServer(host, port, app, request_handler,
                              passthrough_errors, ssl_context, fd=fd)

然后在inner方法中,srv.serve_forever()是服务运行起来

我们看下我们的示例中,最简单那个例子

application1 = Response('Hello World application1!')

为什么设置run_simple('localhost', 4000, application1),当接受请求时,为什么会执行application1的对象内方法,并且返回给浏览器
因为所有的 python web 框架都要遵循 WSGI 协议,WSGI 中有一个非常重要的概念:每个 python web 应用都是一个可调用(callable)的对象,要运行 web 应用,必须有 web server,在werkzeug中提供了 WSGIServer,
Server 和 Application 之间怎么通信,就是 WSGI 的功能

wsgi有两方,服务器方 和 应用程序
①服务器方:其调用应用程序,给应用程序提供(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递给服务器方.

②应用程序:用来生成返回的header,body和status,以便返回给服务器方。

所以在我们示例代码中,当run_simple('localhost', 4000, hello)执行后,当http://localhost:4000/请求时,就会触发application1 = Response('Hello World application1!')
我们接下来看下werkzeug.wrappers.py模块下的Response

class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,
               CommonResponseDescriptorsMixin,
               WWWAuthenticateMixin):

该类是多重继承类,这里主要看下BaseResponse,先看下初始方法

    def __init__(self, response=None, status=None, headers=None,
                 mimetype=None, content_type=None, direct_passthrough=False):
        if isinstance(headers, Headers):
            self.headers = headers
        elif not headers:
            self.headers = Headers()
        else:
            self.headers = Headers(headers)

        if content_type is None:
            if mimetype is None and 'content-type' not in self.headers:
                mimetype = self.default_mimetype
            if mimetype is not None:
                mimetype = get_content_type(mimetype, self.charset)
            content_type = mimetype
        if content_type is not None:
            self.headers['Content-Type'] = content_type
        if status is None:
            status = self.default_status
        if isinstance(status, integer_types):
            self.status_code = status
        else:
            self.status = status

        self.direct_passthrough = direct_passthrough
        self._on_close = []

        # we set the response after the headers so that if a class changes
        # the charset attribute, the data is set in the correct charset.
        if response is None:
            self.response = []
        elif isinstance(response, (text_type, bytes, bytearray)):
            self.set_data(response)
        else:
            self.response = response

BaseResponse__init__初始方法中,我们定义了返回的Headerscontent_type、状态码status,最后通过self.set_data(response),跟踪代码如下:

    def set_data(self, value):
        if isinstance(value, text_type):
            value = value.encode(self.charset)
        else:
            value = bytes(value)
        self.response = [value]
        if self.automatically_set_content_length:
            self.headers['Content-Length'] = str(len(value))

将我们示例中的application1 = Response('Hello World application1!')参数字符串,进行bytes类型转换进行传输,
然后执行对象(),在调用__call__方法,

  def __call__(self, environ, start_response):
        print(start_response)
        app_iter, status, headers = self.get_wsgi_response(environ)
        start_response(status, headers)
        return app_iter

这里要先介绍一个environ参数,
在我们的示例中有3种方法来实现,而我利用了最简单的方式1去讲解,然而方式1参数中没有涉及到environ参数

#方式一
application1 = Response('Hello World application1!')

#方式二
def application2(environ, start_response):
    request = Request(environ)
    response = Response("Hello %s!" % request.args.get('name', 'World!'))
    return response(environ, start_response)

#方式三
@Request.application
def hello(request):
    return Response('Hello World Request!')

以上方式2中涉及到了environ,其实这个environ参数是包含了请求的所有信息,

让我们在看下__call__方法中, app_iter, status, headers = self.get_wsgi_response(environ)输出
通过请求系列参数,获取最后要返回的get_wsgi_response,输出如下:

<werkzeug.wsgi.ClosingIterator object at 0x0589C0B0> --- 200 OK --- [('Content-Type'\\\省略]

然后在start_response(status, headers)代码中,start_response 是 application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息
让我们来看下start_response输出,

<function WSGIRequestHandler.run_wsgi.<locals>.start_response at 0x05A32108>

跟踪代码如下start_response

 def start_response(status, response_headers, exc_info=None):
            if exc_info:
                try:
                    if headers_sent:
                        reraise(*exc_info)
                finally:
                    exc_info = None
            elif headers_set:
                raise AssertionError('Headers already set')
            headers_set[:] = [status, response_headers]
            return write

start_response返回write方法,然后跟踪该方法

        def write(data):
            assert headers_set, 'write() before start_response'
            if not headers_sent:
                status, response_headers = headers_sent[:] = headers_set
                try:
                    code, msg = status.split(None, 1)
                except ValueError:
                    code, msg = status, ""
                code = int(code)
                self.send_response(code, msg)
                header_keys = set()
                for key, value in response_headers:
                    self.send_header(key, value)
                    key = key.lower()
                    header_keys.add(key)
                if not ('content-length' in header_keys or
                        environ['REQUEST_METHOD'] == 'HEAD' or
                        code < 200 or code in (204, 304)):
                    self.close_connection = True
                    self.send_header('Connection', 'close')
                if 'server' not in header_keys:
                    self.send_header('Server', self.version_string())
                if 'date' not in header_keys:
                    self.send_header('Date', self.date_time_string())
                self.end_headers()

            assert isinstance(data, bytes), 'applications must write bytes'
            self.wfile.write(data)
            self.wfile.flush()

最后就输出到浏览器

以上就是简单的请求、响应流程

Flask示例

我们在Flask中会按着如下使用

from flask import Flask


app = Flask(__name__) 

@app.route('/index')
def index():
    return 'Hello World'

if __name__ == '__main__':
    app.run() # run_simple(host,port,app)

跟进run方法

    def run(self, host=None, port=None, debug=None, **options):

        from werkzeug.serving import run_simple
        if host is None:
            host = '127.0.0.1'
        if port is None:
            server_name = self.config['SERVER_NAME']
            if server_name and ':' in server_name:
                port = int(server_name.rsplit(':', 1)[1])
            else:
                port = 5000
        if debug is not None:
            self.debug = bool(debug)
        options.setdefault('use_reloader', self.debug)
        options.setdefault('use_debugger', self.debug)
        try:
            run_simple(host, port, self, **options)
        finally:

            self._got_first_request = False

我们看到最后依然是执行的run_simple(host, port, self, **options),也就是werkzeug.serving.py下的run_simple方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值