0%

Python-Flask入门

背景

​ 团队准备开发QA工具平台,一方面锻炼编程能力,一方面是提高团队的工作效率,如可测性、数据构造等(避免仅仅是写一些重复的接口用例)。团队成员的现状如下:

  • 比较熟悉的是Python语言;
  • 以前基本没有工具平台的开发经验。

根据上述的背景来看,在进行整体技术栈选型的时候以下两点比较重要。

  • 上手速度要快;
  • 社区比较活跃,相关文档比较多。

框架选型

在进行后端框架的选型之前,先简单的对框架进行一些了解。

框架(Framework)是一个框子——指其约束性,也是一个架子——指其支撑性

它本身一般不完整到可以解决特定问题,其主要的特性是为了扩展各种功能已满足用户的需求。

框架选择

Python的常用Web框架挺多,但在Github上比较火的有 DjangoFlaskTornado 与前两者的star数量基本已经相差好几倍了)。

  • Django 一一 企业级开发框架

    诞生于2003年,相对于其他WEB框架来说,它主要优点是功能齐全,缺点则是模块之间紧密耦合,开发者需要学习Django自己定义的这一整套技术。

  • Flask 一一 快速建站

    诞生于2010年,其定义就是面向简单需求和小型应用的微框架(啥叫微框架,就是毛坯房的意思。给你个毛胚房,你自己装修去),这样的好处就是具有较高的扩展性。

  • Tornado 一一 高并发处理

    诞生于2009年9月10日,其主要特点是:提供了异步I/O支持、超时事件处理。

  • Twisted、Pyramid、Web2py等

不同框架的区别

  • Full-Stack Web框架:框架功能很全,不用自己造轮子,比如(cache、session、登陆、auth授权等等)以及它强大的中间件,提供全方案Web开发支持。比如Django框架

    当然功能强大和全面的反面就是有点复杂(相对的),不太灵活。所以Django上手要慢一点,自己造一个轮子替换Django某些内置功能或者使用第三方功能时不太灵活。

  • Non Full-Stack Web框架:框架小巧,灵活,很多功能需要开发者以插件的形式向里安装,也可以自己定制,比如Flask框架

  • Asynchronous 异步框架:框架本身的速度比较快,I/O性能吞吐高并发,当然异步编程的理解难度要大一点。像TornadoSanic框架

为什么选择Flask

      截至2019年9月2日,Flask在Github上的星数是46179颗,Django的Github星数是43806颗,两者几乎难分伯仲,其它Python Web框架与Flask和Django星数相差甚远。考虑到Django早发布5年,而Flask在星数上还领先2000多颗,由此可以得知Flask当前略微占优。

选Flask框架的原因主要基于如下几个点:

  1. 简约的设计哲学 一一 Less is more

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from flask import Flask
    app = Flask(__name__)

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

    if __name__ == '__main__':
    app.run()
  2. 通俗易懂的 快速入门

  3. 装饰器表示路由。(面向切面编程-JAVA中的AOP)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @app.route('/')
    def hello_world():
    return 'Hello World!'


    @app.route('/add', methods=['POST'])
    def compute_add():
    params = request.get_json().get('params')
    if not params or not isinstance(params, dict):
    return "params is error"
    else:
    return str(params.setdefault('a', 0) + params.setdefault('b', 0))

    通过本地Debug可以明显发现,代码启动后自动通过 app.route('xxx') 就自动将前端访问的路由地址与后端的代码给绑定到了一起,并且在后续的请求中,通过装饰器自动定位到具体的后端方法。

  1. 蓝图 一一 Blueprint

    常见的情况:当功能多了后就会出现如下代码的样式,很多个路由都在一个文件中,维护以及可读性都很差。这个时候就需要方便的管理这些路由。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @app.route('/add')
    def hello_world1():
    return 'Hello World!'

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

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

    ...

    蓝图便类似一个有效的菜单栏,通过将不同的功能的子菜单在不同的文件中单独维护,然后集中在通过 app.register\_blueprint(xxx) 注册到代码中。

    1
    2
    3
    4
    5
    6
    7
    from flask import Blueprint, jsonify

    compute_method_api = Blueprint('compute_method', __name__)

    @compute_method_api.route("/compute_add")
    def get_sonar_error_info():
    return jsonify(1 + 1)

通过上面介绍,在需要开发的平台不太复杂的情况下,Flask比较符合新手作为快速入门的框架(个人意见)。

Flask 框架介绍

​ Flask是一种使用Python编写的轻量级的Web框架,WSGI工具采用Werkzeug,模板引擎使用Jinja2,下图表示 WSGI 的一个过程。

首先从官网的一个最小的 Flask应用进行分析,运行如下代码:
1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask(__name__)

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

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

(在不去了解内部原理的情况下)光从代码来看,仅仅做了如下几点:

  1. 首先,导入了 Flask 类,这个类的对象将会是我们的 WSGI 应用程序。
  2. 接下来,创建一个该类的实例对象,第一个参数是应用模块或者包的名称。这样 Flask 才知道到哪去找模板、静态文件等等。详情见 Flask 的文档。
  3. 然后,我们使用 route() 装饰器告诉 Flask 什么样的 URL 能触发我们的函数。这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
  4. 最后我们用run()函数来让应用运行在本地服务器上。

那具体上面的Demo代码实现原理是如何呢?具体分为3个步骤,分别对应着上面的代码层面的逻辑: 见如下图:

  • 实例一个Flask类的对象,里面进行了各种参数的初始化,主要讲下里面如下几个参数:

    1. self.static_url_path:用于改变url的path,默认静态文件放在static下面,所以url是static/filename 。

      1
      2
      app = Flask(__name__, static_url_path="/static/change")
      # 如上指定static_url_path路径后那么默认的static/filename就访问不到静态资源了,必须通过static/change/filename才能访问到。
    2. self.static_folder: 用来改变静态资源目录,默认是static目录下。

      1
      2
      app = Flask(__name__, static_folder="my_static")
      # static_folder="my_static" 那么默认的static/filename就访问不到静态资源了,必须通过my_static/filename才能访问到静态资源。
    3. self.blueprints: 用于更好的管理多个Api文件。(更多相关可以自行搜索Flask 蓝图

      举个例子:如果把所有的路由都如Demo代码里面的app.route一样写到一个文件中,那随着项目的增加,此文件的维护性会越来越差,所以通过先将不同的模块都分别写到对应的文件中,然后通过蓝图的注册到Flask实例对象中的self.blueprints

    4. self.url_map: 用于存储所有的路由规则。

  • @app.route(‘xxx’) 进行所有路由规则进行添加。

    基本的原理就是通过解析所有带有route装饰器的方法,通过将 rulefunction_name 进行一一绑定并且存储到self.url_map中,大概的存储格式如下图:(rule为route(‘xxx’)中的xxx, endpoint为绑定的方法名,像Demo代码中 rule 为 ‘/‘, endpoint 为 ‘hello_world’)

  • app.run(host=’0.0.0.0’, port=5000) 启动应用。

    从源码中发现flask启动server的时候实际就是直接使用的 Werkzeug.serving 的 run_simple(…),在这里可以简单的理解内部启动了一个BaseWSGIServer。

在浏览器中输入 http://0.0.0.0:5000/ 就会返回Hello World了。那Flask又是如何处理的浏览器发起的请求的呢?大概原理如下图:

整个处理请求的逻辑简单的可以看分为两部分:

  • Werkzeug.serving

    简单点说就是创建一个线程,并且为此次请求实例化一个通用处理器 BaseRequestHandler 用于初始化此次请求的信息等,然后直接调用Flask.app去执行该请求,最终通过start_response对结果进行相应处理后返回。

  • Flask.app

    • Full_dispatch_request: 对请求进行预处理(如蓝图)以及结果的格式化。
    • Dispatch_request: 根据self.view_functions里面的Rule找到指定的Function,然后执行func返回结果。
    • Make_response:将dispatch_request处理完的返回值变成符合http协议的响应结果。
    1
    2
    3
    4
    5
    6
    7
    8
    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)

下图梳理了 Flask 框架的一个整体流程图(有兴趣的可以更深入的去了解下相关内容)。

分享内容_photo_2.png

参考链接

------------- 本 文 结 束 感 谢 您 的 阅 读 -------------