Flask -- 01. werkzeug请求与响应以及源码的解析

Posted yzm1017

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask -- 01. werkzeug请求与响应以及源码的解析相关的知识,希望对你有一定的参考价值。

werkzeug:

Flask框架内部本身没有实现socket,而是使用wsgi实现。wsgi是web服务网管接口,能够对请求进行封装、解析。

基于werkzeug的web应用:

# 方式一:Flask返回对象是Response, 继承于werkzeug的BaseResponse。

from werkzeug.serving import run_simple
from werkzeug.wrappers import BaseResponse

def hello(environ, start_response):
    # environ, start_response参数在下面有说明
    response = BaseResponse('Hello World!')
    return response(environ, start_response)

if __name__ == '__main__':
    run_simple('127.0.0.1', 5000, hello)
# 方式二:
from werkzeug.wrappers import Request, Response

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

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 4000, hello)

源码分析:

  1. 首先看一下初始化 __init__.py类,它重新构造 object_origins字典数据格式,列举了以下键值对:

    # BaseResponse - --- werkzeug.wrappers
    # BaseRequest - --- werkzeug.wrappers
    # Request - --- werkzeug.wrappers
    # Response - --- werkzeug.wrappers 
    
    # 该字典的键是werkzeug下的某模块中的函数、方法,值是werkzeug下的某模块
  2. 在文件起始处我们引入了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:如何进行传输数据加密,可以设置的环境if use_reloader:
  3. 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()
  4. 通过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
            )
  5. 然后在inner方法中,srv.serve_forever()使服务运行起来 。

  6. run_simple(‘localhost‘, 4000, hello)执行后,在http://localhost:4000/请求到来时,就会触发Response(‘Hello World!‘)

  7. 接下来我们看一下 werkzeug.wrappers.py模块下的Response

    class Response(
        BaseResponse,
        ETagResponseMixin,
        ResponseStreamMixin,
        CommonResponseDescriptorsMixin,
        WWWAuthenticateMixin,
    ) 
  8. 该类是多重继承类,主要看一下BaseResponse:

    首先__init__初始化时,定义了返回的Headerscontent_type、状态码status等.

    def __init__(
            self,
            response=None,
            status=None,
            headers=None,
            mimetype=None,
            content_type=None,
            direct_passthrough=False,
        )

    最后通过self.set_data(response),跟踪代码如下:

    def set_data(self, value):
            """Sets a new string as response.  The value set must either by a
            unicode or bytestring.  If a unicode string is set it's encoded
            automatically to the charset of the response (utf-8 by default).
    
            .. versionadded:: 0.9
            """
            # if an unicode string is set, it's encoded directly so that we
            # can set the content length
        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))

    我们在例子中的Response(‘Hello World!‘) 参数字符串,会以bytes类型进行数据的传输。

  9. 然后执行对象(),会调用__call__方法, 会返回一个应用程序迭代器

    def __call__(self, environ, start_response):
        """Process this response as WSGI application.
    
        :param environ: the WSGI environment.
        :param start_response: the response callable provided by the WSGI server.
        :return: an application iterator
        """
        app_iter, status, headers = self.get_wsgi_response(environ)
        start_response(status, headers)
        return app_iter

最上面的例子中,environ参数是包含了请求的所有信息,start_response 是 application 请求处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息 。

以上是关于Flask -- 01. werkzeug请求与响应以及源码的解析的主要内容,如果未能解决你的问题,请参考以下文章

Flask之WSGI:Werkzeug

flask 中的 werkzeug Local,LocalStack 和 LocalProxy 技术应用

Flask

flask学习总结

Flask快速入门

M1-Flask-Day1