Flask系列分享:路由的实现之 post 请求实现各种数据上传(三)

在上一章

# 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 格式的请求数据。你可以使用 Flaskrequest.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>

相关推荐
狗头大军之江苏分军1 小时前
《潮水漫过十七岁》开学了
后端
Patrick在香港1 小时前
Claude Prompt 香港场景:公文里「今日」落在 6 个日历日上,「翌日」锚错了 5 天
python·自然语言处理·正则表达式·claude·数据清洗
苏三说技术1 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong2 小时前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno7042 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc2 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
YsyaaabB2 小时前
Python 数值分析
python
阿洛学长2 小时前
计算机二级 Python 基本操作题(15 分)真题笔记(0101 ~ 1903 全套)
python·pycharm
weixin199701080162 小时前
[特殊字符]️《二手ERP对接电商平台的总体方案:统一数据模型 + 事件驱动 + 灰度上线6原则》(附Python源码)
大数据·python
aramae3 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端