flask 框架源码浅析结构(二)重点 WSGI 源代码分析(待续)

本文深入分析了Flask框架中的Werkzeug,介绍了其作为WSGI服务器的角色和主要功能,包括请求对象、响应对象、路由系统以及HTTP实用工具。通过逐步拆解`application`、`run`、`wsgi_app`和`full_dispatch_request`等关键函数,揭示了Flask处理HTTP请求的工作流程。文章以代码实例和流程图辅助理解,为读者提供了一个了解Flask内部机制的起点。
摘要由CSDN通过智能技术生成

flask 框架源码浅析结构(二)重点 WSGI 源代码分析

上一节,我们已经了解到了flask是个什么东西,以及里面的一些名词,我知道,对于大部分人来说,当然也包括我,刚开始接触这个东西就感觉到压力山大,但是,其实只要用心去走他的每一层 ,去抓每一个return,将其做成图,就能更好的便于我们理解与剖析flask框架的源码(直接切入正题)

Werkzeug(点这里到官方网站)

Werkzeug是什么:

官方网站对于werkzeug的定义的翻译了解它里面有什么,才能更好的用

一种交互式调试器,允许在浏览器中使用交互式解释器检查堆栈跟踪和源代码,以查看堆栈中的任何帧。

一个功能齐全的请求对象,包含与头、查询参数、表单数据、文件和cookie交互的对象。

可以包装其他wsgi应用程序并处理流数据的响应对象。

一种路由系统,用于将url与端点匹配并生成端点的url,具有一个可扩展的系统,用于从url捕获变量。

用于处理实体标记、缓存控制、日期、用户代理、cookie、文件等的http实用程序。

在本地开发应用程序时使用的线程化wsgi服务器。

一种测试客户端,用于在测试期间模拟http请求,而无需运行服务器。

Werkzeug支持Unicode,不强制任何依赖项。由开发人员来选择模板引擎、数据库适配器,甚至如何处理请求。它可以用于构建各种最终用户应用程序,如博客、wiki或公告板。
我截取了官方网站用来做测试的一段代码

#代码片段一
from werkzeug.wrappers import Request, Response

@Request.application
def application(request):
    return Response("Hello, World!")

if __name__ == "__main__":
    from werkzeug.serving import run_simple
    run_simple("localhost", 5000, application)

运行结果
如果我们在浏览器上访问 http://127.0.0.1:5000/上可以看到“hello,world"内容

然而我们要研究的,就是 这个用户访问http://127.0.0.1:5000/后浏览“Hello World"这个过程Flask的工作原理及代码框架

拆applicattion函数
  • 首先我们发现在这个代码区中有个函数 application(request)来 那我们就打开它(其中一些我已经进行了翻译)
def application(environ, start_response):

 response_body = 'The request method was %s' % environ['REQUEST_METHOD']

 # HTTP response code and message
 status = '200 OK'

 # 应答的头部是一个列表,每对键值都必须是一个 tuple。
 response_headers = [('Content-Type', 'text/plain'),
                     ('Content-Length', str(len(response_body)))]

 # 调用服务器程序提供的 start_response,填入两个参数
 start_response(status, response_headers)

 # 返回必须是 iterable
 return [response_body]

#2. 可调用对象是一个类实例
class AppClass:
   """这里的可调用对象就是 AppClass 的实例,使用方法类似于: 
       app = AppClass()
       for result in app(environ, start_response):
           do_somthing(result)
   """

   def __init__(self):
       pass

   def __call__(self, environ, start_response):
       status = '200 OK'
       response_headers = [('Content-type', 'text/plain')]
       self.start(status, response_headers)
       yield "Hello world!\n"

WSGI接口的作用是确保HTTP请求能够转化成python应用的一个功能调用,这也就是Gateway的意义所在,网关的作用就是在协议之前进行转换。
WSGI接口中有一个非常明确的标准,每个Python Web应用必须是可调用callable的对象且返回一个iterator,并实现了app(environ, start_response) 的接口,server 会调用 application,并传给它两个参数:environ 包含了请求的所有信息,start_response 是 application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息

拆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:
            # reset the first request information if the development server
            # reset normally.  This makes it possible to restart the server
            # without reloader and that stuff from an interactive shell.
            self._got_first_request = False
           
拆wsgi_app
def wsgi_app(self, environ, start_response):
    ctx = self.request_context(environ)
    ctx.push()
    error = None
    try:
        try:
            response = self.full_dispatch_request()
        except Exception as e:
            error = e
            response = self.handle_exception(e)
        return response(environ, start_response)
    finally:
        if self.should_ignore_error(error):
            error = None
        ctx.auto_pop(error)

我们拆开来看 四个步骤

  • ctx = self.request_context(environ)创建请求上下文,并把它推送到栈中,在上一篇有提及

  • response = self.full_dispatch_request()处理请求,通过flask的路由寻找对应的视图函数进行处理(上一篇应该有印象)

  • 通过try…except封装处理步骤2的处理函数,如果有问题,抛出500错误。

  • ctx.auto_pop(error)当前请求退栈。

  • 下来拆full_dispatch_request()

拆full_dispatch_request()
def full_dispatch_request(self):
    self.try_trigger_before_first_request_functions()
    try:
        request_started.send(self)
        rv = self.preprocess_request()
        if rv is None:
            rv = self.dispatch_request()
    except Exception as e:
        rv = self.handle_user_exception(e)
    return self.finalize_request(rv)

来 我们拆开看

1.self.try_trigger_before_first_request_functions()触发第一次请求之前需要处理的函数,只会执行一次。
2. self.preprocess_request()触发用户设置的在请求处理之前需要执行的函数,这个可以通过@app.before_request来设置
3. rv = self.dispatch_request() 核心的处理函数,包括了路由的匹配,下面会展开来讲
4. rv = self.handle_user_exception(e) 处理异常
5.return self.finalize_request(rv),将返回的结果转换成Response对象并返回。
6. 接下来我们看dispatch_request函数:

拆dispatch_request
    def dispatch_request(self):
        """此处省略翻译500字
        """
        req = _request_ctx_stack.top.request
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        rule = req.url_rule
        # if we provide automatic options for this URL and the
        # request came with the OPTIONS method, reply automatically
        if (
            getattr(rule, "provide_automatic_options", False)
            and req.method == "OPTIONS"
        ):
            return self.make_default_options_response()
        # otherwise dispatch to the handler for that endpoint
        return self.view_functions[rule.endpoint](**req.view_args)

处理的逻辑如下:

  1. req = _request_ctx_stack.top.request获得请求对象,并检查有效性。
  2. 对于请求的方法进行判断,如果HTTP请求时OPTIONS类型且用户未设置provide_automatic_options=False,则进入默认的OPTIONS请求回应,否则请求endpoint匹配的函数执行,并返回内容。
    在上述的处理逻辑中,Flask从请求上下文中获得匹配的rule,这是如何实现的呢,请看上节“上下文”。
歇会明天拆

在这里插入图片描述在这里插入图片描述
附一张不完整的解码图
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值