1.Flask的请求
如果以GET请求访问URL,例如URL是127.0.0.1:5000/?name=andy&age=18,那么如何获取这个URL的参数?如果以POST请求提交一个表单,那么又如何获取表单中各个字段值呢?
Flask提供的Request请求对象就可以实现上述功能
Request请求对象的常用属性和方法
属性或方法 | 说明 |
args | 存储url请求中的查询参数,返回类似于字典的数据 |
method | 存储请求中使用的HTTP方法,例如GET或POST |
form | 存储请求提交的所有表单数据,返回类似于字典的数据 |
files | 存储请求上传的所有文件,返回类似于字典的数据 |
url | 存储客户端请求的完整URL |
host | 存储请求的域名 |
headers | 存储HTTP请求的请求头信息,返回类似于字典的数据 |
cookies | 存储请求的所有cookie,返回类似于字典的数据 |
1.1 GET请求
使用request.args.get()方法就可以获取GET请求参数
实例1:获取GET请求参数
获取127.0.0.1:5000/?name=andy&age=18这个URL中的name和age两个参数,在资源文件目录下创建一个run.py文件
代码如下所示
# run.py
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
name = request.args.get('name')
age = request.args.get('age')
message = f'姓名:{name}\n年龄:{age}'
return message
if __name__ == '__main__':
app.run(debug=True)
运行run.py文件,在浏览器中访问网址: 127.0.0.1:5000/?name=andy&age=18,运行结果如下图所示
1.2 POST请求
使用request.args.post()方法可以接收POST请求参数,如果接收表单数据,可以使用request.from对象
实例2: 获取表单提交信息
首先创建一个run.py文件,在该文件中定义一个login路由,代码如下所示
# run.py
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
message = f'用户名是:{username}</br>密码是:{password}'
return message
return render_template('login.html')
if __name__ == '__main__':
app.run(debug=True)
然后在run.py同级目录下创建"templates"文件夹,在"templates"路径下创建login.html模板文件
代码如下所示
<!--login.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<form action="" method="post">
<div>
<label for="username">用户名</label>
<input type="text" id="username" name="username" value="">
</div>
<div>
<label for="password">密 码</label>
<input type="password" id="password" name="password" value="">
</div>
<button type="submit">提交</button>
</form>
</body>
</html>
运行run.py文件,在浏览器中访问网址127.0.0.1:5000/login,显示表单页面,用户名输入andy,密码输入123456
单击【提交】按钮,运行结果如下所示
文件上传
在使用Web表单时,经常会使用到上传文件功能,request.files对象可以获取与表单相关的数据
案例3: 实现上传用户图片功能
首先定义一个路由函数upload()用于上传图片,然后提交上传的图片,在另一个路由函数uploaded_file()中显示图片内容,具体操作步骤如下
① 在"templates"目录下创建文件上传模板upload.html,代码如下所示
<!--upload.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传图片</title>
</head>
<body>
<!--form表单中设置enctype="multipart/form-data",用于上传文件-->
<form action="" method="post" enctype="multipart/form-data">
<div>
<label for="avatar">上传图片</label>
<input type="file" id="avatar" name="avatar" value="">
</div>
<button type="submit">提交</button>
</form>
</body>
</html>
② 创建一个run.py文件,在该文件中编写upload()路由函数,当接受GET请求时,显示模板文件内容;当接受POST请求时,上传图片
代码如下所示
@app.route('/upload',methods=['GET','POST'])
def upload():
"""
头像上传表单页面
:return:
"""
if request.method == 'POST':
# 接受头像字段
avatar = request.files['avatar']
# 判断文件是否上传,已经上传文件类型是否正确
if avatar and allowed_file(avatar.filename):
# 生成一个随机文件名
filename = random_file(avatar.filename)
# 保存文件
avatar.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file',filename=filename))
return render_template('upload.html')
上述代码中,使用request.files['avatar']接收表单中name='avatar'的字段值,然后检测用户是否上传了图片,并且使用allowed_file()函数检测上传的文件类型是否满足设定的要求
allowed_file()函数代码如下所示
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
def allowed_file(filename):
"""
判断上传文件类型是否允许
:param filename: 文件名
:return: 布尔值True或False
"""
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
接下来使用random_file()函数为上传的文件重新创建一个随机的不重复的文件名,通过使用uuid.uuid4()生产处一个随机的几乎不可能重复的文件名,然后拼接一个完整的路径,代码如下所示
import uuid
def random_file(filename):
"""
生成随机文件
:param filename: 文件名
:return: 随机文件名
"""
# 获取文件后缀
ext = os.path.splitext(filename)[1]
# 使用uuid生成随机字符
new_filename = uuid.uuid4().hex+ext
return new_filename
准备工作完成后,最后调用avartar.save()方法将图片存储到相应的路径下
③ 在run.py文件中创建uploaded_file()路由函数,显示图片内容,代码如下所示
@app.route('/uploads/<filename>')
def uploaded_file(filename):
"""
显示上传头像
:param filename: 文件名
:return: 真实文件路径
"""
return send_from_directory(app.config['UPLOAD_FOLDER'],filename)
send_from_directory()函数用于显示静态资源文件
上述run.py文件完整代码如下所示
# run.py
import os
import uuid
from flask import send_from_directory
from flask import Flask, request, render_template, redirect, url_for
app = Flask(__name__)
UPLOAD_FOLDER = os.path.join(app.root_path, 'uploads')
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
"""
判断上传文件类型是否允许
:param filename: 文件名
:return: 布尔值True或False
"""
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
def random_file(filename):
"""
生成随机文件
:param filename: 文件名
:return: 随机文件名
"""
# 获取文件后缀
ext = os.path.splitext(filename)[1]
# 使用uuid生成随机字符
new_filename = uuid.uuid4().hex+ext
return new_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload():
"""
头像上传表单页面
"""
if request.method == 'POST':
# 接受头像字段
avatar = request.files['avatar']
# 判断文件是否上传,已经上传文件类型是否正确
if avatar and allowed_file(avatar.filename):
# 生成一个随机文件名
filename = random_file(avatar.filename)
# 保存文件
avatar.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file', filename=filename))
return render_template('upload.html')
@app.route('/uploads/<filename>')
def uploaded_file(filename):
"""
显示上传头像
:param filename: 文件名
:return: 真实文件路径
"""
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
if __name__ == '__main__':
app.run(debug=True)
执行run.py程序文件,在浏览器中输入网址: http://127.0.0.1:5000/upload,显示上传图片页面,单击【浏览】按钮,弹出一个选择框,选择一个图片后,单击【打开】按钮,会将图片文件名显示在【浏览】按钮右侧
单击【提交】按钮上传文件,如果上传成功,页面将跳转至uploads,在该页面显示图片内容,运行结果如下图所示
此时,图片被上传到设置的路径下,并创建了一个随机的文件名
1.3 钩子函数的应用
有时需要对请求进行预处理(preprocessing)和后处理(postprocessing),这时可以使用Flask提供的一些请求钩子(Hook),它们可以用来注册在请求处理的不同阶段执行的处理函数
Flask的请求钩子指的是在执行视图函数前后执行的一些函数,我们可以在这些函数里面做一些操作
Flask利用装饰器给我们提供了四种钩子函数
before_first_request: 在处理第一个请求前执行,比如链接数据库操作
before_request: 在每次请求前执行,比如权限校验
after_request: 每次请求之后调用,前提是没有未处理的异常抛出
teardown_request: 每次请求之后调用,即使有未处理的异常抛出
实例4: 使用请求钩子,在执行视图函数前后执行相应的函数
创建一个run.py文件,在该文件中定义一个index视图函数,然后定义before_first_request、before_request、after_request和teardown_request四个钩子,代码如下所示
# run.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
print('视图函数执行')
return 'index page'
# 在第一次请求之前运行
@app.before_first_request
def before_first_request():
print('before_first_request')
# 在每一次请求前都会执行
@app.before_request
def before_request():
print('before_request')
# 在请求之后运行
@app.after_request
def after_request(response):
# response就是前面的请求处理完毕之后,返回的响应数据,前提是视图函数没有出现异常
print('after_request')
return response
# 无论视图函数是否出现异常,每一次请求之后都会调用,会接收一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(error):
print('teardown_request: error %s' % error)
if __name__ == '__main__':
app.run(debug=True)
执行run.py程序文件,第1次在浏览器中访问网址127.0.0.1:5000时,控制台输出结果如下
before_first_request
before_request
视图函数执行
after_request
teardown_request: error None
第2次在浏览器中访问网址127.0.0.1:5000时,控制台输出结果如下
before_request
视图函数执行
after_request
teardown_request: error None
请求钩子的一些常见应用场景
1.运行程序前需要进行一些程序的初始化操作,比如创建数据库表,添加管理员用户等,这些工作可以放到使用before_first_request装饰器注册的函数中
2.网站上要记录用户最后在线的时间,可以通过用户最后发送的请求时间来实现。为了避免在每个视图函数都添加更新在线时间的代码,可以仅在使用before_request钩子注册的函数中调用这段代码
3.在视图函数中进行数据库操作时,比如更新、插入等操作,之后需要将更改提交到数据库中,提交更改的代码就可以放到after_request钩子注册的函数中
2.Flask的响应
Response响应对象常用的属性和方法
属性或方法 | 说明 |
headers | 响应首部 |
status | 状态 |
status_code | 状态码,文本类型 |
mimetype | MIME类型 |
set_cookie | 设置Cookie |
get_json | 解析为JSON数据 |
is_json | 判断是否为JSON数据 |
2.1 接收响应
响应在Flask中使用Response响应对象表示,响应报文中的大部分内容由服务器处理,大多数情况下,我们只负责返回主体内容
当在浏览器中输入一个网址时,Flask会先判断是否可以找到与请求URL相匹配的路由,如果没有,则返回404响应。如果找到,则调用相应的视图函数
视图函数的返回值构成了响应报文的主体内容,当请求成功时,返回状态码默认为200
最常见的响应可以只包含主体内容,示例代码如下所示
@app.route('/index')
def index():
return render_template('index.html')
响应还可以返回带状态码的形式,示例代码如下所示
@app.errorhandler(404)
def page_note_found(e):
return render_template('404.html')
在大多数情况下,除了响应主体,其他部分我们通常只需要使用默认值即可
2.2 响应的格式
在HTTP响应中,数据可以通过多种格式传输
不同的响应数据格式需要设置不同的MIME类型,MIME类型在首部的Content-Type字段中定义
我们以默认的HTML类型为例,Content-Type内容如下
Content-Type: text/html; charset=utf-8
但是在特定的情况下,也会使用其他格式。我们可以通过Flask提供的make_response()方法生成响应对象,传入响应的主体作为参数,然后使用响应对象的mimetype属性设置MIME类型
常用的数据格式有纯文本、HTML、XML和JSON,它们对应的MIME类型如下
纯文本:text/plain
HTML: text/html
XML:application/xml
JSON:application/json
实例5
# run.py
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/index')
def index():
response = make_response('Hello World')
response.mimetype = 'text/plain'
return response
if __name__ == '__main__':
app.run(debug=True)
运行run.py文件,在浏览器中访问网址127.0.0.1:5000/index,结果如下图所示
实例6
# run.py
from flask import Flask, make_response, json
app = Flask(__name__)
@app.route('/index')
def index():
data = {'name':'Andy', 'age':18}
# 调用dumps()方法将字典、列表或元组序列化(serialize)为JSON字符串
response = make_response(json.dumps(data))
response.mimetype = 'application/json'
return response
if __name__ == '__main__':
app.run(debug=True)
运行run.py文件,在浏览器中访问网址127.0.0.1:5000/index,结果如下图所示
我们也可以使用Flask提供的jsonify()函数,只要传入数据或参数,它会对传入的参数进行序列化,转换成JSON字符串作为响应的主体,然后生成一个响应对象,并且设置正确的MIME类型
# run.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/index')
def index():
# return jsonify({'name':'Andy', 'age':18})
return jsonify(name='Andy', age=18)
if __name__ == '__main__':
app.run(debug=True)
运行run.py文件,在浏览器中访问网址127.0.0.1:5000/index,结果如下图所示