IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Flask源码解析(二):Flask的工作流程 -> 正文阅读

[Python知识库]Flask源码解析(二):Flask的工作流程

本文通过解析Flask0.1的源码,讲解一下Flask框架的主要工作流程。为了方便理解,后面涉及到的部分源码只保留核心部分,要看完整版可以点这里:Flask0.1的源码

启动应用

我们先看一下 Flask 的简单使用:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == "__main__":
    app.run()

可以看出应用启动的代码是app.run(),这个方法的代码如下:

def run(self, host='localhost', port=5000, **options):
    from werkzeug import run_simple
    return run_simple(host, port, self, **options)

run()方法只是调用了werkzeug的run_simple()方法,把self作为参数传进去了,这里的self就是上面创建的Flask对象。

run_simple()方法会启动一个WSGI服务器。关于WSGI的概念,上一篇 Flask源码解析(一):WSGI 中已经写过了,这里就不再赘述了。要说一下的是,WSGI服务器的主要工作包括:监听指定的端口,接收到HTTP请求,根据WSGI协议解析请求数据并调用app对象。
werkzeug.serving:WSGIRequestHandlerrun_wsgi()方法中可以看到调用app的逻辑如下:

def execute(app):
    application_iter = app(environ, start_response)
    try:
        for data in application_iter:
            write(data)
        # make sure the headers are sent
        if not headers_sent:
            write('')
    finally:
        if hasattr(application_iter, 'close'):
            application_iter.close()
        application_iter = None

可以看到,这里把app对象作为方法调用,其实就相当于调用了Flask类的__call__方法:

def __call__(self, environ, start_response):
    return self.wsgi_app(environ, start_response)

请求处理

wsgi_app()方法中会进行一次HTTP请求的核心处理流程:

def wsgi_app(self, environ, start_response):
    with self.request_context(environ):
        rv = self.preprocess_request()
        if rv is None:
            rv = self.dispatch_request()
        response = self.make_response(rv)
        response = self.process_response(response)
        return response(environ, start_response)

下面就分别介绍wsgi_app()方法中调用的各个方法。

创建上下文

with self.request_context(environ)的作用是创建上下文,关于上下文的内容不在这里展开,之后会专门写一篇文章来讲。

请求预处理

当使用before_request装饰器时,会将装饰的函数加入到before_request_funcs列表中来:

def before_request(self, f):
    """Registers a function to run before each request."""
    self.before_request_funcs.append(f)
    return f

preprocess_request()会遍历执行before_request装饰的函数:

def preprocess_request(self):
    for func in self.before_request_funcs:
        rv = func()
        if rv is not None:
            return rv

注意:只要有一个预处理函数返回非None,则不再执行后面的函数,直接返回结果,也不再执行dispatch_request()进行请求调度。

请求调度

dispatch_request()会找到对应的处理函数,调用并返回处理函数的结果:

def dispatch_request(self):
    endpoint, values = self.match_request()
    return self.view_functions[endpoint](**values)

构造Response对象

make_response()将处理函数的结果转换为Response类型的对象

def make_response(self, rv):
    if isinstance(rv, self.response_class):
        return rv
    if isinstance(rv, basestring):
        return self.response_class(rv)
    if isinstance(rv, tuple):
        return self.response_class(*rv)
    return self.response_class.force_type(rv, request.environ)

请求后处理

当使用after_request装饰器时,会将装饰的函数加入到after_request_funcs列表中来:

def after_request(self, f):
    """Register a function to be run after each request."""
    self.after_request_funcs.append(f)
    return f

process_response()方法先将session的信息保存到response中,然后遍历执行after_request装饰的函数

def process_response(self, response):
    session = _request_ctx_stack.top.session
    if session is not None:
        self.save_session(session, response)
    for handler in self.after_request_funcs:
        response = handler(response)
    return response

路由

路由就是根据请求的 URL 找到对应处理函数的过程。上面讲到dispatch_request()会找到对应的处理函数,那它是如何找到的呢。

我们先来了解一下路由规则的注册,定义处理函数的时候我们会用app.route()装饰器来注册路由规则:

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

我们来看看app.route()方法中做了什么:

def route(self, rule, **options):
    def decorator(f):
        self.add_url_rule(rule, f.__name__, **options)
        self.view_functions[f.__name__] = f
        return f
    return decorator
    
def add_url_rule(self, rule, endpoint, **options):
    options['endpoint'] = endpoint
    options.setdefault('methods', ('GET',))
    self.url_map.add(Rule(rule, **options))

可以这样简单理解,route装饰器会在url_map中保存把url规则和函数名的映射关系,在view_functions中保存函数名和函数对象的映射关系。

然后我们回过头来看一下dispatch_request()方法,继续分析路由的过程:

def dispatch_request(self):
    endpoint, values = self.match_request()
    return self.view_functions[endpoint](**values)

def match_request(self):
   rv = _request_ctx_stack.top.url_adapter.match()
   request.endpoint, request.view_args = rv
   return rv

match_request()方法会调用url_adapter.match()获取到函数名endpoint和参数values,然后根据endpointview_functions获取处理函数并调用。
这个url_adapter对象是在请求上下文中初始化的,代码如下:

 class _RequestContext(object):
    def __init__(self, app, environ):
        self.app = app
        self.url_adapter = app.url_map.bind_to_environ(environ)
        self.request = app.request_class(environ)
        self.session = app.open_session(self.request)
        self.g = _RequestGlobals()
        self.flashes = None

结合以上两段代码,可以看出获取endpoint和参数是由url_map.bind_to_environ(environ).match()来实现的。
url_map中存储的是url与endpoint之间的映射关系,而environ中保存着请求信息,包括请求的url。可以这样理解,存储信息的对象url_map根据environ中的url进行匹配,就能获取到请求对应的endpoint和参数。

异常处理

dispatch_request()的完整代码其实还包括异常处理(上面的是缩略版):

def dispatch_request(self):
    try:
        endpoint, values = self.match_request()
        return self.view_functions[endpoint](**values)
    except HTTPException, e:
        handler = self.error_handlers.get(e.code)
        if handler is None:
            return e
        return handler(e)
    except Exception, e:
        handler = self.error_handlers.get(500)
        if self.debug or handler is None:
            raise
        return handler(e)

当使用errorhandler装饰器时,会将装饰的函数加入到error_handlers字典中来,这里的code参数是http状态码

def errorhandler(self, code):
    def decorator(f):
        self.error_handlers[code] = f
        return f
    return decorator

dispatch_request()方法中,当match_request()方法或处理函数抛出异常时,会根据不同情况进行处理:

  1. 当抛出的异常类型是HTTPException
    1. 如果根据http状态码能在error_handlers里找到对应的处理函数,则调用处理函数并返回结果
    2. 没有找到的话则直接返回这个异常,之后会由make_response()方法处理成http状态码对应的报错页面
  2. 当抛出的异常类型不是HTTPException
    1. 如果是debug模式或找不到处理函数,则把异常直接抛出给WerkZeug框架
      1. 如果是debug模式,WerkZeug会打印报错堆栈信息,并返回报错堆栈页面
      2. 如果是非debug模式,WerkZeug会打印报错堆栈信息,并返回状态码500对应的Internal Server Error页面
    2. 如果不是debug模式且找到了处理函数,则调用处理函数并返回结果
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:24:12  更:2022-04-24 09:24:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 17:52:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码