在上一章
# Flask系列分享:路由的实现之 post 请求的使用(二)
已经介绍了 Flask post 请求的三种请求方式:
- 无参请求
- 地址栏携带参数(这种请求方式,按照写路由地址的格式传入参数。所以目前看来只有 Flask 框架经过了处理,能这样请求。其他 Python 后端框架就不知道能不能这样传递了。)
- 请求请求体携带参数请求(application/json)。
启动 Flask
python
import os
form flask import Flask
app = Flask(os.getcwd())
if __name__ == "__main__":
app.run(host="0.0.0.0", prox=9999, debug=True)
1. 下面介绍其它三种请求数据格式
1.1 application/x-www-form-urlencoded:
可以理解为是 application/json 的另一种数据传输格式,application/json 的数据格式,是将数据放入 http 请求体中。而 application/x-www-form-urlencoded 是将 post 请求的 json 数据按照字符串的格式拼接在请求的url地址后面,进行请求。格式如下: http://127.0.0.1:9999/add?name=小明&age=18&citry=武汉
首键值对使用?链接,后面不管有多少个键值对都以&符号链接。
但是这种数据格式请求,虽然能在请求地址中一目了然键值对,但是也受制于服务端默认限制大小,不能携带过多的数据。而且如果是重要数据,这种请求方式将用户的数据曝光的一清二楚。所以不建议使用 application/x-www-form-urlencoded 请求去做复杂数据和重要数据请求。
python
@app.post("/login")
def index():
content_type = request.headers["Content-Type"]
if content_type == "application/json":
data = request.json
elif content_type == "application/x-www-form-urlencoded":
data = request.form.to_dict()
isLogin = data.get("phone") == "18800000000" and data.get("password") == "123456"
return {"msg": f"登录{'成功' if isLogin else '失败'}"}
1.2 multipart/form-data
1.2.1 简单的使用
python
@app.post("/login")
def index():
data = json.loads(request.data)
isLogin = data.get("phone") == "18800000000" and data.get("password") == "123123"
return {"msg": f"登录{'成功' if isLogin else '失败'}"}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" id="myForm">
<label>
手机号:
<input type="text" value="18800000000" name="phone" />
</label>
<br />
<label>
密码:
<input type="password" value="123123" name="password" />
</label>
<br />
<button id="btn">登录</button>
</form>
<div id="msg"></div>
<script>
btn.onclick = function (e) {
e.preventDefault();
const [{ value: phone }, { value: password }] = myForm.elements;
const formData = new FormData(myForm);
fetch("login", {
method: "POST",
body: formData,
})
.then((res) => res.json())
.then((res) => {
msg.innerText = res.msg;
});
};
</script>
</body>
</html>
1.2.2 参数和文件一起上传
在 Flask 如果上传的 formData 中有文件数据的话,在 Flask 中使用 request 类能够很好的获取到上传的文件。
python
@app.post("/login")
def index():
data = request.form
# 使用 request.files.keys() 获取所有上传的文件数据字段名,
file_keys_list = list(request.files.keys())
# 判断 temp 文件夹是否存在,如果不存在,怎创建 temp 文件夹,用来存储文件。
if os.path.exists(os.path.join(os.getcwd(), "temp")) is False:
os.mkdir(os.path.join(os.getcwd(), "temp"))
if len(file_keys_list) > 0:
```
循环 file_keys_list,再使用 request.files.getlist(key) 获取所有属性名的文件值,返回一个
list 集合,集合中的每个元素类型都是处理之后的 FileStorage 类。
```
for key in file_keys_list:
file_list = request.files.getlist(key)
if len(file_list) > 0:
for file in file_list:
# 使用 FileStorage 类自带的 save 函数将文件数据存储到指定的文件夹中。
file.save(
os.path.join(os.getcwd(), f"temp\\{int(time.time())}_{file.filename}")
)
isLogin = data.get("phone") == "18800000000" and data.get("password") == "123123"
return {"msg": f"登录{'成功' if isLogin else '失败'}"}
1.2.3 text/plain
纯文本传输,文件设置为纯文本的形式,浏览器在获取到这种文件时并不会对其进行处理。
使用场景,在数据不需要任何处理时,就可以使用 text/plain 传输纯文本数据给服务端。
比如要发送的邮箱内容,和指令发送,日志记录等等。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form method="post" enctype="text/plain" id="myForm">
<label>
手机号:
<input type="text" value="18800000000" name="phone" />
</label>
<br />
<label>
密码:
<input type="password" value="123123" name="password" />
</label>
<br />
<input type="file" name="img" id="file" multiple />
<br />
<button id="btn">登录</button>
</form>
<div id="msg"></div>
<script>
btn.onclick = function (e) {
e.preventDefault();
const [{ value: phone }, { value: password }] = myForm.elements;
fetch("login", {
method: "POST",
headers: {
"Content-Type": "text/plain",
},
body: {
phone,
password,
},
})
.then((res) => res.json())
.then((res) => {
msg.innerText = res.msg;
});
};
</script>
</body>
</html>
python
@app.post("/login")
def login():
# 使用 request.get_data() 获取 text/plain 纯文本数据
# as_text -> True,传参告知是否为纯文本数据,get_data 函数根据 as_text 值来处理纯文本数据。
data = request.get_data(as_text=True)
print(data)
return {"msg": "登录成功"}
可以看出浏览器传送过去的数据是未经处理的纯文本给服务器。
在控制台打印 data 时,可以看出打印的数据和浏览器传过来的数据是一致的。
1.2.4 text/html
python
@app.post("/login")
def login():
data = request.get_data(as_text=True)
return jsonify({"data": data})
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="btn">传输html</button>
<div id="result"></div>
<script>
btn.onclick = function (e) {
fetch("login", {
method: "POST",
headers: {
"Content-Type": "text/html",
},
body: "<h1>hello world</h1>",
})
.then((res) => res.text())
.then((res) => {
res = JSON.parse(res).data;
result.innerHTML += res;
});
};
</script>
</body>
</html>
在 html 中使用 fetch 传输了一段 html 代码给服务端,服务端获取到,再响应给客户端,然后客户端将这段 html 代码追加到指定元素中。
1.2.5 text/xml
text/xml
内容类型在 Flask 中的使用场景主要涉及到处理和返回 XML 数据。这种数据格式通常用于与需要 XML 格式的客户端(例如某些旧的系统或特定的 API)进行交互。以下是如何在 Flask 中处理 text/xml
内容类型的示例。
场景 1:处理 text/xml
请求数据
假设你有一个 API 端点,它接收 text/xml
格式的请求数据。你可以使用 Flask 的 request.data
读取原始的请求体,然后使用一个 XML 解析库(例如 xml.etree.ElementTree
)来解析 XML 数据。
python
@app.post("/login")
def xml_endpoint():
content_type = request.headers.get("Content-Type")
if content_type == "text/xml":
# 读取原始的 XML 数据
xml_data = request.data
try:
# 解析 XML 数据
root = ET.fromstring(xml_data)
# 提取 phone 和 password 元素的文本内容
phone = root.find("phone").text
password = root.find("password").text
# 进行一些逻辑处理
is_login = phone == "18800000000" and password == "123123"
response_msg = f"登录{'成功' if is_login else '失败'}"
except ET.ParseError:
return Response(
"<error>Invalid XML</error>", status=400, mimetype="text/xml"
)
else:
return Response(
"<error>Unsupported Media Type</error>", status=415, mimetype="text/xml"
)
# 返回 XML 响应
response_xml = f"<response><message>{response_msg}</message></response>"
return Response(response_xml, mimetype="text/xml")
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form method="post" enctype="text/plain" id="myForm">
<label>
手机号:
<input type="text" value="18800000000" name="phone" />
</label>
<br />
<label>
密码:
<input type="password" value="123123" name="password" />
</label>
<br />
<button id="btn">登录</button>
</form>
<div id="msg"></div>
<script>
/*
这里使用了 DOMParser 的实例方法 parseFromString 来解析后端返回的 xml 数据。
DOMParser 文档地址: https://developer.mozilla.org/zh-CN/docs/Web/API/DOMParser
*/
const parser = new DOMParser();
btn.onclick = function (e) {
e.preventDefault();
const [{ value: phone }, { value: password }] = myForm.elements;
fetch("login", {
method: "POST",
headers: {
"Content-Type": "text/xml",
},
body: `
<credentials>
<phone>${phone}</phone>
<password>${password}</password>
</credentials>
`,
})
.then((res) => res.text())
.then(async (res) => {
const xmlDoc = parser.parseFromString(res, "application/xml");
const childNodes = xmlDoc.documentElement.childNodes;
const obj = {};
for (let i = 0; i < childNodes.length; i++) {
const childNode = childNodes[i];
obj[childNode.tagName] = childNode.textContent;
}
msg.innerText = obj.message;
});
};
</script>
</body>
</html>