Flask 学习笔记(一)

一、Web 服务器与 Web 框架

首先明确一下,要运行一个动态网页,我们需要

  1. 一个 Web 服务器来监听并响应请求,如果请求的是静态文件它就直接将其返回,如果是动态 url 它就将请求转交给 Web 应用。
  2. 一个 Web 应用来动态处理请求,生成响应

其中 Web 服务器通常都是别人已经实现好了的,它通过定义好的接口与我们编写的 Web 应用通信。WSGI 就是一个统一的 Web 服务器接口标准,如果我们按照 WSGI 编写 Web 应用,那么它就能在任何符合该标准的服务器上运行,例如 Gunicorn.(对比一下 Java 的 Servlet,按照 Servlet 规范编写的应用,都能运行在任何 Servlet 容器上,例如 Tomcat 和 Jetty,Servlet 容器就相当于 WSGI 服务器)

可是 WSGI 仍然比较底层,直接照着它写太麻烦,于是就有了 Web 框架,Python 知名的就是 Flask 和 Django。Java 的 Servlet 也是如此,对应的出现了 Spring MVC 框架。但是 Flask 和 Django 都有内置服务器用于测试,而 Spring MVC 没有,倒是 Spring Boot 可以使用内嵌的 tomcat 容器。

二、Flask

Flask 是一个微框架,“微”是指它的核心非常小,任何可选的功能都不包括在内。但是 Flask 社区提供了丰富的拓展插件,你可以通过选择需要的插件来实现你想要的功能。

中间略过 n 万字。。。

三、上下文 Context

上下文 Context,是动态创建的东西。flask 通过四个全局代理对象,提供对上下文的访问:

  1. current_app 和 g:应用上下文
  2. request 和 session:请求上下文

1. 请求上下文 Request Context

Java 的 Spring MVC 将请求作为方法参数传入,而且要求参数必须映射到某个类型或者某个 model 上。
Flask 的处理方式与之不同,它提供了一个全局代理对象——request,只要是在请求从开始到结束的过程中,都可以直接通过这个 request 访问 HTTP 请求的各种参数。

请求上下文在请求开始时被压入栈,这时 request 对象才可用,在请求结束后会立即被 pop 出来。

疑问

为什么 request 得是一个代理对象呢?

  • 在视图函数被 load 时,很可能连 app 都还没有创建(如果用工厂模式 create_app 的话),更不可能存在 request 了。
    • 因此 request 只能是一个占位符(代理对象),等到请求真正到来时,flask 再将请求数据 push 到 request_stack 内,这时 request 对象就可用了。

2. 应用上下文 Application Context

一个 app,就是一个 Flask 实例,通过 app = Flask(__name__) 创建。

在请求到来时,除了请求上下文被入栈,还有应用上下文也会被入栈。
请求结束时,它先 pop 出请求上下文,然后是应用上下文。

也就是说在请求过程中,应用上下文是可用的,current_app 就是当前 app 实例的代理,对它进行任何操作,都会直接影响到 app 本身!!(current_app 的修改是全局的!)

疑问

在 flask 初始化时,我们不是已经通过 app = Flask(__name__) 创建了一个 app 实例了么?直接用它不行么?

  1. 在工厂模式下,app = Flask(__name__) 会在 create_app() 被调用时,才会执行。
    • 可是在这之前,整个 Web 项目就会被初始化。初始化时根本不存在 app 实例。因此只能先用个 current_app 做占位符(代理对象)
  2. 编写可重用的 blueprint 时,你不知道要从哪里导入 app 实例。如果直接用 current_app,就不需要去管 app 什么时候在哪被创建了。

3. g 对象(应用上下文)

g 对象,名称来源于 global,保存在应用上下文中,指应用上下文中的全局变量。(其实每个请求都拥有独立的 g 对象)

如果我们需要在当前请求中传递数据(请求结束,数据就销毁),是不能使用 current_app 的,因为对 current_app 的操作会直接影响到 app 本身。
但是应用上下文本身的生命周期,是和请求上下文相同的,flask 在应用上下文中提供了 g 对象,该对象不是什么别的代理!!因此对它的操作,在请求结束时,就会连同 current_app 一起被销毁。

4. session 对象(请求上下文)

session 位于 请求上下文中。它的内容会被保存到 cookie 中,发送到客户端。每次收到请求时又会从 cookie 中加载它。因此它可用于跨请求传输数据。

这种 session 被称为客户端 session(client side session)

session 被存入 cookie 前,会使用你设定的 SECRET_KEY 签名,这样才能确认请求中的 session 就是 flask 发送出去的,没有被人修改过,或者说不是伪造的。(除非你的 SECRET_KEY 泄漏了。。)

但是签名不是数据加密,session 的内容很容易被解码出来,因此 session 只适合存放非敏感数据!!!下面是 session 解密代码:

#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)

    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True

    try:
        payload = base64_decode(payload)
    except Exception as e:
        raise Exception('Could not base64 decode the payload because of '
                         'an exception')

    if decompress:
        try:
            payload = zlib.decompress(payload)
        except Exception as e:
            raise Exception('Could not zlib decompress the payload before '
                             'decoding the payload')

    return session_json_serializer.loads(payload)

if __name__ == '__main__':
    print(decryption(sys.argv[1].encode()))

通过命令行传入你的 session,它就能直接给出内容!

如果你确实需要在 session 中存放敏感数据(比如验证码),那么你可以阅读一下参考链接中的文章

四、插件

  1. python-dotenv:配置文件
  2. flask_wtform:表单
  3. flask-login:登录与权限验证
  4. flask-sqlalchemy + flask-migrate:数据库
  5. flask-rest-api:restful api
  6. flask-Sockets:websocket
  7. flask-mail: 邮件发送
  8. flask-limiter: ip 访问频率限制(防爬)
  9. flask-security: 整合了 flask-login flask-mail 等一系列用户验证相关的插件,提供完善的权限验证功能。
  10. flask-admin: 可以很方便的构建网站管理后台
  11. flask-caching: 缓存

四、使用 blueprint(蓝图)

入门级 flask app 都是单文件应用,当复杂度上升,我们可以把单文件应用分成多个文件,比如:views.py、models.py、forms.py、errors.py。

但是如果项目继续增大,各个文件也会渐渐变得难以维护。views.py 中各种功能的视图函数混在一起,models.py 和 forms.py 也是。显然我们可以按功能,继续分割这几个代码文件。

blueprint 就是用于应对大型项目开发的功能,使用它可以对上述的文件按功能做进一步的拆分。

蓝图使用起来就像应用当中的子应用一样,可以有自己的模板,静态目录,有自己的视图函数和URL规则,蓝图之间互相不影响。但是它们又属于应用中,可以共享应用的配置。

实际上 BluePrint 类的使用也几乎和 Flask 一模一样。差别只是 BluePrint 最后还需要在 Flask 实例中注册:app.register_blueprint(<blue_print_obj>).

注意事项

  1. url_for 的第一个参数 endpoint,是 view function 的名称,不是 route 路径!
    • 使用 blueprint 时,endpoint 为 蓝图名称.视图函数名称
  2. 读取资源文件:资源文件可以放在 data 文件夹下,使用 flask.open_resources("data/<file_name>", mode="rb") 读取资源文件。
    • 不放在 static 下的原因是,该目录内的文件是可以直接通过 URI /static/<file_path> 访问的,是公开的。我们一般不希望用户直接访问这种资源文件。
  3. flask 的内置服务器和 gunicorn 都不支持 http2、keep-alive(http1.1),也没有 gzip 实体压缩。这些功能通常都通过使用 nginx 做反向代理来获得。
    • gunicorn 对此的解释是,gunicorn 就是设计用来放在 nginx 后面运行的,而 nginx 与它的 upstream 通信时默认使用 http1.0

链接

posted @ 2019-02-07 20:24  於清樂  阅读(1335)  评论(0编辑  收藏  举报