Flask网络框架介绍

概述

  Flask是一个轻量级、灵活和高度可扩展的web应用框架。它由一个健壮的核心来提供web需要的基础功能,然后将期望的其他功能由第三方扩展模块提供,这与python语言的特性相似。

基础功能

  路由(routes)机制是指为每一个http请求的URL映射(map)处理函数(handler),具体实现则使用python的修饰符或者app.add_url_rule()方法。下面的代码例子非常简单直接,它为根路径指定index()处理函数,该函数直接返回一段HTML语句到客户端,这种处理函数称之为视图(view)函数。

1
2
3
4
5
6
7
8
app = Flask(__name__)

@app.route('/')
def index():
return '<h1>Hello World!</h1>'

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

这很少的几行代码已经可以运行一个web服务器!
  Flask通过route修饰符的语法可以支持三种类型的URLs。route修饰符将URL看作由静态部分与动态部分组成,动态部分由<>标记,用作视图函数的参数。例如:

1
2
3
@app.route('/user/<name>')
def user(name):
return '<h1>Hello World %s!</h1>' % name

所以user可以处理所有静态部分为’/user/‘的URL。另外route修饰符的参数也可以设置Request的类型,如GET型或者POST型。

  Flask使用context机制来提供一些变量,这些变量被看作是全局(global)的,可以直接访问,比如Http请求的一些信息可以访问request变量获得。context分为Application context和Request context,前者包括一些面向整个应用的变量(如g, current_app),后者包括一些仅面向某次http请求的变量(如reauest、session)。同时也不难理解,前者的变量只能在应用启动激活后才能访问,后者只能在某次来自客户端的request请求期间才可以访问。

  Flask使用Request Hooks机制来绑定一些函数,这些函数可以在http请求被分发(dispatching)到视图函数之前或者之后执行,这些函数可以执行一些通用的任务,可以避免相同的任务代码必须包含在所有的视图函数中,以此增加代码的重用性。

  app.config字典是一个通用的地方来存放配置变量。其中SECRET_KEY变量被Flask或第三方模块用作加密key,例如保护网站免受CSRF攻击的Flask-WTF模块。

  Blueprint机制是Flask为了支持动态生成应用实例(Application Instance)以及应用工厂(Application Factory)模式而产生的。app实例不再在全局代码的开头生成,而是由外部模块调用函数生成,因此view函数不能再使用app修饰符,因为此时app实例还不存在。Blueprint类似于Application,但它所定义的route处于休眠状态直到该Blueprint在Application中注册,例如:

1
2
3
4
5
6
7
8
9
10
11
main = Blueprint('main', __name__)

@main.route('/')
def index():
return '....'

def create_app():
# ...
app = Flask(__name__)
app.register_blueprint(main)
# ...

每一个Blueprint实际上生成了一个自己的命名空间(Namesapce),因此在不同的Blueprint中可以包含同名的view函数。

Jinja2模板引擎

  Flask支持将业务逻辑(business logic)与表示逻辑(presentation logic)分离。对数据库的操作是最常见的业务逻辑,而生成返回到客户端的HTML代码称为表示逻辑,将表示逻辑使用模板(template)可以提高应用的可维护性。
  模块文件包含静态部分和一些动态变量,这些动态变量的值由具体的Request context决定,将变量替换为实际的值并返回最后的Response的过程称之为渲染(rendering)。render_template方法是Jinja2引擎的主要接口。
  Jinja2支持任何类型的动态变量,包括list、dictionaries或者object等。Jinja2支持过滤器(filer),例如,其中capitalize将name的首字母转换成大写字母。其中safe过滤器可以防止特殊字符串比如HTML代码中被解析,否则恶意用户可以在最后的response中插入被执行的恶意代码。Jinja2支持控制结构,如if、for等,也支持宏(macro)功能,它类似于python的函数功能。Jinja2支持在任何地方引用另一个模块文件,来增加代码重用性,也支持模块继承功能。
  在模板文件中直接写入某些Link的URL地址会导致非常大的不灵活性,因此Flask提供url_for()方法从URL map中生成实际的URL地址。
  从另外一种角度看,模块引擎实际上是一种嵌入在HTML代码中的另外一种语言,它要在静态页面中增加动态内容的目的是和其他语言,如PHP,JSP等是相同的。从语言的角度理解模板引擎更能认识到它所带来的便利性,以及它本身不可避免地的带来的复杂性。

Flask-WTF表单处理模块

  Form类或其子类来表示web表单,该类定义了一系列域来表示表单中的对象,并通过validators函数来对域进行合法性检查。例如表单中的Text对象可以用StringField类表示,并使用Required()来检测非空合法性。表单的这种封装形式便于使用模板渲染。
  Post/Redirect/Get 模式是为了防止客户端刷新页面时发送重复的Post请求的机制。服务端首先处理Post请求,将Post请求中的表单数据存入session变量(Request context变量)中,然后返回Redirect报文;客户端接受到Redirect报文后发送Get请求,服务端会读取session变量中的数据并生成最后的包含HTML代码的Response。

数据库模块

  在选择数据库的问题上,ORMs和ODMs是一种值得注意的模型。它们可以将在对象(object)的操作转换为底层的数据库SQL命令,开发者可以不用自己编写SQL命令代码。这种机制可能会影响一些程序的性能,但在易用性、可移植性等方面却有着优势。
  Flask-SQLAlchemy模块是Flask对SQLAlchmy ORM的一种扩展。Model类可以被python的类继承来表示一个表,类中的属性(attributes)会转换为表的schema的属性(Column)。这些类的属性可以设置表名,也可以设置约束,如主键约束、外健引用、值域等,对应于编写数据库表定义的DDT文件。ForeignKey类和relationship方法可以表示数据库表与表的关系,如一对多(one-to-many)、多对一(many-to-one)和多对多(many-to-many)。插入、删除、查询和更新等操作都可以通过flask-sqlachemy提供的API完成,它们在运行过程中会执行底层的SQL命令,如果出现错误,也会抛出异常给用户。这些API可以直接在view函数中使用,来完成相应的业务逻辑。另外Pagination类支持分页功能。
  数据库迁移(database migration)框架Flask-Migrate用来跟踪数据库表schema的变化。在应用开发过程中,Model类的属性可能会增加、减少或更新,这导致数据库表的schema定义也要发生变化,而且要保证原有的数据不能丢失,migrate被用来帮助开发者有效率地解决这样的问题。

Werkzeug模块

  Werkzeug的security模块可以提供基于HASH的安全密码(secure password hashing)。
Werkzeug 是WSGI模块

其他模块

Flask-script模块为Flask增加了命令行解析器。
Flask-Moment模块提高时间和日期的本地化(Localization)。
Flask-login模块可以完成用户登陆、认证和保存登陆状态的功能。
itsdangerous模块提供加密token,可以用于身份认证。
Flask-pagedown模块支持MarkDown文本编辑器。
Flask-HTTPAuth模块提供RESTful的用户认证功能。

REST

  对于RIA(Rich Internet Application)架构,客户端承担更多的业务逻辑,而服务端主要任务是提供数据访问和存储。RIA和服务端通信的协议可以使用RPC,如XML-RPC或者SOAP,REST(Representational State Transfer)也是一个流行的选择。Flask可以容易地实现RESTful的web服务器。REST架构的风格:

  1. 将客户端与服务端分开
  2. 无状态(Stateless):即服务端不保存任何客户端在不同的Request间传递的状态信息,而客户端的每次Request必须包含所有必要的信息。
  3. 缓存化(Cache):服务端的Response可以被标记是否缓存。
  4. 统一接口(Uniform Interface):客户端和服务端的通信协议必须一致、良好定义和标准化。
  5. 分层系统:客户端和服务器间可以插入代理(Proxy),来提高性能、可用性和可扩展性。
  6. Code-On-Demand:客户端可以从服务端下载代码并在自己的上下文中执行。

在REST中,资源(Resource)是核心概念,每一个资源URL必须与唯一的一个资源相互对应。资源的集合也可以对应一个URL,这个URL一般以’/‘结尾,来表达这些资源集合在一个文件夹中。另外,在REST架构中,GET请求用来获取单个资源或者资源集合;POST请求新增一个资源并把它加入某个集合中;PUT请求更新一个资源;DELETE请求删除一个资源或者资源集合。REST不严格要求资源的编码格式,客户端和服务端可以通过HTTP协商,常用的格式包括JSON和XML。REST的资源架构类似于WWW,在那里 ,一个资源中会包含许多指向其他资源的链接,整个WWW就是资源作为节点形成的巨大网络。
  在REST中,Versioning指REST服务端的接口可以版本化,并且在URL中包含版本号,这样做可以使得服务端根据版本号服务不同的客户端,这些客户端可能使用了最新的API,而另外一些却没有。
  由于REST是无状态的,因此它不能按照传统的方法来进行用户认证和访问控制。REST中,客户端的每一个请求都必须携带必要的认证信息,比如可以包含在HTTP的Authorization头部中。基于Token的认证可以避免将敏感信息频繁地出现在来往的报文中,客户端首先发送携带口令的请求,得到带有Token的回复,然后后续请求都只需要携带这个token来进行认证,token如果超期,客户端重新携带口令请求一个新的token。另外,服务端返回到客户端的资源不一定要和数据库的资源完全相同,有些安全敏感但对客户端无用的信息可以不包含在Response中。

应用开发结构

  Flask鼓励开发者按照自己的需求设定自己的项目文件和结构布局。
  工厂模式(Factory Pattern)。
  requirements.txt文件中记录应用所有的包依赖(Package Dependencies),它可以由pip自动生成,也就可以作为pip的install文件来自动安装所有的依赖包。
  单元测试(UnitTests),可以使用python unittest库。