第14天: Web 开发 Flask 介绍

WEB 开发是现在程序必会的技能,因为大部分软件都以 Web 形式提供,及时制作后台开发,或者只做前台开发,也需要了解 Web 开发的概念和特点。 由于 Python 是解释性脚本语言,用来做 Web 开发非常适合,而且 Python 有上百中 Web 开发框架,以及成熟的模板技术,使得Web开发如虎添翼。今天借用 Flask 框架,快速学习一下 Python 的 Web 开发知识。

Flask 框架

Flask 的设计易于使用和扩展。它的初衷是为各种复杂的Web应用程序构建坚实的基础。可以自由地插入任何扩展。也可以自由构建自己的模块。Flask 适合各种项目。它对原型设计特别有用。Flask 依赖于两个外部库:Jinja2 模板引擎和 Werkzeug WSGI 工具包。 Flask 是最精致,功能最丰富的微框架之一。Flask 还很年轻,拥有蓬勃发展的社区,一流的扩展和漂亮的 API。Flask 具有快速模板,强大的 WSGI 功能,在 Web 应用程序和库级别的完整单元可测性,以及大量文档等优点。 选用 Flask 框架也是因为它方便入手,结构简单,零配置,是个学习 Python Web 开发的好工具。

安装 Flask

像其他模块一样,Flask 的安装很简单,下面通过 pip 包管理器来安装

pip install flask

检查一下是否安装正确,在命令行下输入 python 进入命令行模式, 引入 flask 模块,回车

>>> import flask
>>>

如果没有错误提醒,就说明安装成功了

Hello world

下面写个最简单的Web应用 hello.py

1
2
3
4
5
6
7
8
9
10
from flask import Flask  # 引入Flask模块

app = Flask(__name__) # 创建一个应用

@app.route('/')
def index():    # 定义根目录处理器
    return '<h1>Hello World!</h1>'

if __name__ == '__main__':
    app.run() # 启动服务

打开终端,跳转到 hello.py 文件所在的文件夹,进入 python 命令行模式,启动服务

>>> python hello.py

如果一起正常的话会有类似下面的反馈

 * Serving Flask app "hello" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

因为是通过 app.run() 启动的服务,所以会有个错误提示,提醒不能将此Web应用部署在生产环境中,可以暂时忽略

此时,打开浏览器,输入127.0.0.1:5000/ 或者 localhost:5000/, 就可以看到 Hello World! 欢迎字样。

路由

路由是 Web 开发中一个很重要的概念,用来将不同的请求,映射到响应的处理方法上,这个方法被称为视图函数。比如刚才的 Hello 应用,将根请求,映射到index 处理方法上,下面简单了解下Flask对路由的支持

Flask通过修饰器(和Java的注解类似)来建立路由映射关系的,已经看到修饰器是 app.rotue()

简单路由

如 访问 /hello

1
2
3
@app.route('/hello')
def hello():
    return 'Hello!'

动态路由

如访问 /user/bob 或者 /user/lily 都会映射到同一视图函数上

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

动态域名中动态的部分可以作为视图函数的参数,也支持多个动态参数,如访问 /user/bob/23

1
2
3
@app.route('/user/<name>/<age>')
def user(name, age):
    return "<h1> Hello, %s, you're %s years old" % (name, age)

还可以指定动态部分的数据类型,如

1
2
3
4
5
6
7
8
9
@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return 'Subpath %s' % escape(subpath)

支持的数据类型

类型 说明
string (默认值) 任何不包含斜杠的文本
int 正整数
float 正浮点数
path 类似 string ,但可以包含斜杠
uuid 接受 UUID 字符串

指定HTTP 方法

HTTP 协议,支持多种 HTTP 方法,例如 HEADOPTIONS,以及常用的 GETPOST 等,Flask自动处理了 HEADOPTIONS,路由默认接受的方法是 GET,如果要匹配其他请求方法,可以在路由方法的 methods 参数来指定

1
2
3
4
5
6
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()

复合路由

也可以将多个路由规则,用于一个视图函数, 如访问 /job/ 和访问 /work/ 效果是一样的

1
2
3
4
@app.route('/job/')
@app.route('/work/')
def show_works():
    return 'This is works page'

再复杂一点的例子

1
2
3
4
@app.route('/users/', defaults={'page': 1})
@app.route('/users/page/<int:page>')
def show_users(page):
    pass

上面的代码表示,当访问 /user/ 或者 /user/page/<pageindex> 时,都会有 show_users 视图函数来处理, 而且还为 /user/ 提供了默认值,即访问 /user/ 相当于访问 /user/page/1

请求和响应

Web 应用,最重要的事情就是处理接收到的请求,并返回响应。Flask 框架也一样,它提供了请求对象 request 和响应对象 response ,可以方便的在视图函数中使用。

请求

Flask 将客户端发送的 HTTP 请求封装成了 request 请求对象,并且使用上下文 (context) 临时将 request 变为全局可访问的,于是在视图还是中,就可以直接使用了。

注意: request 并非真正的全局变量!试想,在多线程服务器中,多个线程同时处理不同客户端发送的不同请求时,每个线程看到的 request 对象必然不同。Falsk 使用上下文让特定的变量在一个线程中全局可访问,与此同时却不会干扰其他线程。

Flask 有两种上下文,分别为程序上下文和请求上下文,各自对应的全局变量如下表:

变量名 上下文类型 备注
current_app 程序上下文 表示当前运行的程序实例
g 程序上下文 处理请求时用作临时存储对象,每次请求都会重新设值
request 请求上下文 客户端发来的request请求对象
session 请求上下文 请求携带的会话信息

使用request对象前,需要先引入

1
from flash import request

request 对象提供了丰富的属性和方法,这里举个例子: 通过使用 method 属性可以操作当前请求方法,通过使用 form 属性处理表单数据(在 POST 或者 PUT 请求 中传输的数据)。以下是使用上述两个属性的例子:

1
2
3
4
5
6
7
8
9
10
11
12
@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

注意:当 form 属性中不存在这个键时会发生什么?会引发一个 KeyError 。 如果不处理这个错误 ,就会显示一个 HTTP 400 Bad Request 错误页面。

如果要操作 URL (如 ?key=value )中提交的参数可以使用 args 属性,如 : searchword = request.args.get('key', '')

请求钩子

有时在处理请求之前或之后执行代码会很有用。例如,在请求开始时,可能需要创建数据库连接或者认证发起请求的用户。为了避免在每个视图函数中都使用重复的代码,Flask 提供了注册通用函数的功能,注册的函数可在请求被分发到视图函数之前或之后调用。 请求钩子使用修饰器实现。Flask 支持以下4种钩子:

  • before_first_request:注册一个函数,在处理第一个请求之前运行。
  • before_request:注册一个函数,在每次请求之前运行。
  • after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
  • teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。

示例: 在接受到第一个请求是,打印一句话:

1
2
3
@app.before_first_request
def first_quest():
    print("run before first request")

在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量g。 例如,before_request 处理程序可以从数据库中加载已登录用户,并将其保存到 g.user 中。随后调用视图函数时,视图函数再使用 g.user 获取用户。

响应

响应是 Web 服务器对请求的一个回应,在 Flask 中,有多种形式的响应。视图函数的返回值会自动转换为一个响应对象。 如果返回值是一个字符串,那么会被 转换为一个包含作为响应体的字符串、一个 200 OK 出错代码 和一个 text/html 类型的响应对象。 如果返回值是一个字典,那么会调用 jsonify() 来产生一个响应。 以下是转换的规则:

  • 如果视图返回的是一个响应对象,那么就直接返回它。
  • 如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。
  • 如果返回的是一个字典,那么调用 jsonify 创建一个响应对象。
  • 如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项目,且项目应当由 (response, status) 、 (response, headers) 或者 (response, status, headers) 组成。 status 的值会重载状态代码, headers 是一个由额外头部值组成的列表 或字典。
  • 如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。

除此之外,还可以通过 make_response() 函数,创建可以响应对象,做更个性的事情。

使用make_response()前,需要先引入

1
from flask import make_response

示例:

  • 响应有元组构成
1
2
3
@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

@app.errorhandler 修饰符,会将一个响应代码映射到一个视图函数上,这里是将404(找不到页面)码,处理成一个个性的错误页面

另外,render_template 是 Flask 的模板函数,简单理解就是格式化一个动态的 html 字符串,关于模板的详细用法,会在模板章节描述

  • 使用 make_response() 包裹返回表达式,获得响应对象,并对该对象 进行修改,然后再返回:
1
2
3
4
5
@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

总结

本文借助 Flask 框架,简要介绍了下 Python Web 开发的基本知识和技术,希望能帮助您快速入门,在 Python 学习的道路上走的更顺畅。后续还会将就 Web 开发的话题,对模板数据库 以及扩展功能等进行讲解,敬请期待!

示例代码

参考

Python Geek Tech wechat
欢迎订阅 Python 技术,这里分享关于 Python 的一切。