对于web应用,客户端与服务端之间的数据交互至关重要。在 Flask 中由全局的 request 对象来提供这些信息。
1 Request对象属性
request中包含了前端发送过来的所有请求数据
- form和data是用来提取请求体数据
- 当前端通过存表单提交的时候content-type:urlencode的时候,所有数据已经被flask封装到了form里面。
- 如果时候form-data多类型数据,可以通过request.data获取
- 通过requset.form可以直接提取请求体中的表单格式的数据, 是一个类字典的对象
- 通过get方法只能拿到多个同名参数的第一个
Request对象的重要属性如下:
属性 | 说明 |
---|---|
form | post请求参数, 存储结构个args一致, 它是一个字典对象,包含表单参数及其值的键和值对。默认是接收post参数, 还可以接收PUT,PATCH参数 |
args | get请求参数,args是一个ImmutableMultiDict对象,类字典结构对象。解析查询字符串的内容,它是问号(?)之后的URL的一部分。 |
cookie | 保存Cookie名称和值的字典对象。 |
files | 与上传文件有关的数据。 |
method | 当前请求方式。 |
path | 路由中的路径 |
url | 完整请求地址 |
base_url | 去掉GET参数的URL |
get_json | 如果前端传的json,可以调此接口,自动把前端传的json转化为字典。使用要求前端传递的content-type:application/json |
2 表单数据
当前请求的 HTTP 方法可通过 method 属性来访问。通过:attr:~flask.request.form 属性来访问表单数据( POST 或 PUT 请求提交的数据)。
在以下示例中,’/’ URL会呈现具有表单的网页(student.html)。
填入的数据会发布到触发 result()函数的’/result’ URL。
results()函数收集字典对象中的request.form中存在的表单数据,并将其发送给result.html。
该模板动态呈现表单数据的HTML表格。
from flask import Flask, render_template
from flask import request
app = Flask(__name__)
@app.route('/')
def student():
return render_template('student.html')
@app.route('/result',methods = ['POST', 'GET'])
def result():
if request.method == 'POST':
result = request.form
return render_template("result.html", result=result)
if __name__ == '__main__':
app.run(debug=True)
student.html的HTML脚本:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<form action = "http://localhost:5000/result" method = "POST">
<p>Name <input type = "text" name = "Name" /></p>
<p>Physics <input type = "text" name = "Physics" /></p>
<p>Chemistry <input type = "text" name = "chemistry" /></p>
<p>Maths <input type ="text" name = "Mathematics" /></p>
<p><input type = "submit" value = "submit" /></p>
</form>
</body>
</html>
result.html的HTML脚本:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<table border = 1>
{% for key, value in result.items() %}
<tr>
<th> {{ key }} </th>
<td> {{ value }} </td>
</tr>
{% endfor %}
</table>
</body>
</html>
运行Python脚本,并在浏览器中输入URL http://localhost:5000/
当点击提交按钮时,表单数据以HTML表格的形式呈现在result.html上
3 Cookies
Cookie以文本文件的形式存储在客户端的计算机上。其目的是记住和跟踪与客户使用相关的数据,以获得更好的访问者体验和网站统计信息。
Request对象包含Cookie的属性。它是所有cookie变量及其对应值的字典对象,客户端已传输。除此之外,cookie还存储其网站的到期时间,路径和域名。
在Flask中,对响应对象设置cookie。使用make_response()函数从视图函数的返回值获取响应对象。之后,使用响应对象的set_cookie()函数来存储cookie。
读回cookie很容易。request.cookies属性的get()方法用于读取cookie。
你可以通过 cookies 属性来访问 Cookies,用响应对象的 set_cookie 方法来设置 Cookies。请求对象的 cookies 属性是一个内容为客户端提交的所有 Cookies 的字典。如果你想使用会话,请不要直接使用 Cookies .
读取 cookies:
from flask import request
@app.route('/')
def index():
username = request.cookies.get('username')
# use cookies.get(key) instead of cookies[key] to not get a
# KeyError if the cookie is missing.
存储 cookies:
from flask import make_response
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
Cookies 是设置在响应对象上的。由于通常视图函数只是返回字符串,之后 Flask 将字符串转换为响应对象。如果你要显式地转换,你可以使用 make_response() 函数然后再进行修改。
用法示例:
当访问’/’ URL时,会打开一个简单的表单index.html,可以设置UserID。
from flask import Flask, render_template, make_response
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/setcookie', methods=["POST", "GET"])
def setcookie():
if request.method == "POST":
user = request.form['nm']
resp = make_response(render_template('readcookie.html'))
resp.set_cookie('UserID', user)
return resp
@app.route('/getcookie')
def getcookie():
name = request.cookies.get('UserID')
return f"<h1>welcome {name} </h1>"
if __name__ == '__main__':
app.run(debug=True)
此HTML页面包含一个文本输入。表单发布到’/ setcookie’ URL。相关联的视图函数设置Cookie名称userID并呈现另一个页面。
<html>
<body>
<form action="/setcookie" method="POST">
<p><h3>输入用户名</h3></p>
<p><input type = 'text' name = 'nm'/></p>
<p><input type = 'submit' value = 'Login'/></p>
</form>
</body>
</html>
readcookie.html中,点击超链接时发布到’/getcookie’ URL,获取cookie内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<a href="/getcookie">Click here to read cookie</a>
</body>
</html>
运行python代码,在浏览器中输入http://127.0.0.1:5000/
点击login
点击超链接
4 会话session
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名。这意味着用户可以查看你 Cookie 的内容,但却不能修改它,除非用户知道签名的密钥。
要使用会话,你需要设置一个密钥。
与Cookie不同,Session(会话)数据存储在服务器上。会话是客户端登录到服务器并注销服务器的时间间隔。需要在该会话中保存的数据会存储在服务器上的临时目录中。
为每个客户端的会话分配会话ID。会话数据存储在cookie的顶部,服务器以加密方式对其进行签名。对于此加密,Flask应用程序需要一个定义的SECRET_KEY。
Session对象也是一个字典对象,包含会话变量和关联值的键值对。
例如,要设置一个’username’会话变量,请使用以下语句:
Session[‘username’] = ’admin’
要释放会话变量,使用pop()方法。
session.pop('username', None)
以下代码是Flask中的会话工作的简单演示。URL '/‘只是提示用户登录,因为未设置会话变量’username’。
from flask import Flask, redirect, session, escape, url_for
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
if "username" in session:
return 'Logged in as %s' % escape(session['username'])
return '''You are not logged in <br><a href = '/login'></b>click here to log in</b></a>'''
当用户浏览到点击login按钮时,因为它是通过GET方法调用的,所以将打开一个登录表单。
表单发送回’/login’,现在会话变量已设置。应用程序重定向到’/’。此时会话变量’username’被找到。
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == "POST":
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
应用程序还包含一个logout()视图函数,它会弹出’username’会话变量。并且跳转到首页
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
设置密钥:
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
执行代码:
if __name__ == '__main__':
app.run(debug=True)
浏览器输入URL:http://127.0.0.1:5000/
点击超链接
点击login
5 重定向和错误
redirect()函数:返回一个响应对象,并将用户重定向到具有指定状态代码的另一个目标位置。
函数说明如下:
Flask.redirect(location, statuscode, response)
- location:应该重定向的目标URL
- statuscode:发送到浏览器的状态码,默认是302,一般不动
- response:实例响应
abort() 函数:放弃请求并返回错误代码
函数说明:
Flask.abort(code)
Code参数采用以下值之一:
- 400 - 用于错误请求
- 401 - 用于未身份验证的
- 403 - Forbidden
- 404 - 未不到
- 406 - 表示不接受
- 415 - 用于不支持的媒体类型
- 429 - 请求过多
用法示例:
from flask import Flask, redirect, session, escape, url_for, abort
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
if "username" in session:
return 'Logged in as %s' % escape(session['username'])
return '''You are not logged in <br><a href = '/login'></b>click here to log in</b></a>'''
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == "POST":
session['username'] = request.form['username']
if session['username'] == 'admin':
return redirect(url_for('index'))
else:
abort(401)
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
if __name__ == '__main__':
app.run(debug=True)
运行代码,浏览器输入URL:http://127.0.0.1:5000
输入非admin用户,页面显示:
默认情况下,错误代码会显示一个黑白的错误页面。如果你要定制错误页面, 可以使用 errorhandler() 装饰器
代码示例:
@app.errorhandler(401)
def aut_not_found(error):
return render_template('auth_not_found.html'), 401
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == "POST":
session['username'] = request.form['username']
if session['username'] == 'admin':
return redirect(url_for('index'))
else:
abort(401)
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
auth_not_found.html内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
page not found, 401 Forbidden!!!
</body>
</html>
当输入用户非admin时,运行输出:
6 响应response
视图函数的返回值会被自动转换为一个响应对象。如果返回值是一个字符串, 它被转换为该字符串为主体的、状态码为 200 OK的 、 MIME 类型是
text/html 的响应对象。Flask 把返回值转换为响应对象的逻辑是这样:
- 如果返回的是一个合法的响应对象,它会从视图直接返回。
- 如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
- 如果返回的是一个元组,且元组中的元素可以提供额外的信息。这样的元组必须是 (response, status, headers) 的形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是一个列表或字典,作为额外的消息标头值。
- 如果上述条件均不满足, Flask 会假设返回值是一个合法的 WSGI 应用程序,并转换为一个请求对象。
常用处理response方式:
(1)使用元祖,返回自定义的响应信息
# 响应体 状态码 响应头
# return "index page", 400, [("aimaile", "1"), ("City", "c1")]
# return "index page", 400, {"aimaile": "2", "City1": "cq"}
# return "index page", 666, {"aimaile": "3", "City1": "cq"}
# return "index page", "666 aimaile status", {"aimaile": "python1", "City1": "cq"}
# return "index page", "666 aimaile status"
(2)使用make_response 来构造响应信息
如果你想在视图里操纵上述步骤结果的响应对象,可以使用 make_response() 函数。
譬如你有这样一个视图:
@app.errorhandler(404)
def not_found(error):
return render_template('error.html'), 404
你只需要把返回值表达式传递给 make_response() ,获取结果对象并修改,然后再返回它:
@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
(3) 返回json
目前最常用的是返回json。终端和平台交互现在一般都是json。
当前很流行前后分离,前端用vue,前后交互也用json,后台只管数据处理,前端只管渲染数据。
jsonify()帮助将字典类型转为json数据
@app.route("/list")
def loveCity():
# json就是字符串
data = {
"city": "cq",
"old": 11
}
# json.dumps(字典) 将python的字典转换为json字符串
# json.loads(字符串) 将字符串转换为python中的字典
# json_str = json.dumps(data)
# return json_str, 200, {"Content-Type": "application/json"}
# jsonify帮助转为json数据,并设置响应头 Content-Type 为application/json
# return jsonify(data)
return jsonify(city="cq", country="china")
7 文件上传/下载
文件上传
在Flask中处理文件上传非常简单。它需要一个HTML表单,其enctype属性设置为“multipart / form-data”,将文件发布到URL。工作流程如下:
- 一个 标签被标记有 enctype=multipart/form-data ,并且在里面包含一个 标签
- 服务端应用通过请求对象上的 files 字典访问文件。
- 使用文件的 save() 方法将文件永久地保存在文件系统上的某处。
目标文件的名称可以是硬编码的,也可以从request.files[file]对象的filename属性中获取。但是,建议使用secure_filename()函数获取它的安全版本。
可以在Flask对象的配置设置中定义默认上传文件夹的路径和上传文件的最大大小。
配置 | 说明 |
---|---|
app.config[‘UPLOAD_FOLDER’] | 定义上传文件夹的路径 |
app.config[‘MAX_CONTENT_PATH’] | 指定要上传的文件的最大大小(以字节为单位) |
upload.html内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<form action="http://localhost:5000/uploader" method="POST" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
上传文件代码:
import os
from flask import Flask, redirect, session, escape, url_for, abort, render_template
from flask import request
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = './uploads'
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
@app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
filename = secure_filename(f.filename)
f.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return 'file uploaded successfully'
else:
return render_template('upload.html')
if __name__ == '__main__':
app.run(debug=True)
在浏览器输入URL:http://localhost:5000/uploader
显示上传文件页面如下:
选择文件后,点击“上传”
查看uploads目录,能看到上传的文件
文件下载
在Flask想要实现文件下载功能,只需要导入
send_from_directory(directory, filename, **options)
注意:
这里主要需要注意的是send_from_directory方法,经过实测,需加参数as_attachment=True,否则对于图片格式、txt格式,会把文件内容直接显示在浏览器,对于xlsx等格式,虽然会下载,但是下载的文件名也不正确。
代码示例:
import os
from flask import Flask, abort, render_template, send_from_directory
from flask import request
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = './uploads'
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
@app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
filename = secure_filename(f.filename)
f.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return 'file uploaded successfully'
else:
return render_template('upload.html')
@app.route('/download/<filename>')
def download(filename):
if request.method == "GET":
if os.path.isfile(os.path.join('uploads', filename)):
return send_from_directory('uploads', filename, as_attachment=True)
abort(404)
if __name__ == '__main__':
app.run(debug=True)
8 钩子函数
flask也是面向切面编程的,类似于django 中间件的功能。钩子函数可用于在每个request前或后,执行一些特殊操作。
常用有以下集中几种钩子函数:
钩子函数 | 说明 |
---|---|
@app.before_first_request | 在第一次请求处理之前先被执行 |
@app.before_request | 在每次请求之前都被执行 |
@app.after_request | 在每次请求(视图函数处理)之后都被执行, 前提是视图函数没有出现异常 |
@app.teardown_request | 在每次请求 (视图函数处理)之后都被执行, 无论视图函数是否出现异常,都被执行, 工作在非调试模式时 debug=False |
参考文献:
https://www.w3cschool.cn/flask/flask_sending_form_data_to_template.html