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>

相关推荐
techlead_krischang15 分钟前
中国软件评测中心最新报告:文心大模型技术、产品、应用全面领跑
后端·go
ljh_a117 分钟前
Django 和 Django REST framework 创建对外 API
python·http·django·flask·tornado
syluxhch17 分钟前
Pycharm的终端(Terminal)中切换到当前项目所在的虚拟环境
ide·python·pycharm
yjjpp230118 分钟前
Django REST Framework(四)DRF APIVIEW
后端·python·django
concisedistinct22 分钟前
大数据开发语言 Scala(四):面向对象编程
大数据·开发语言·后端·scala·编程语言·面向对象
铁匠匠匠23 分钟前
django学习入门系列之第三点《BootSrap初了解》
前端·经验分享·笔记·python·学习·django·前端框架
2401_8570262331 分钟前
【Conda与Pip的完美融合】在Conda环境中优雅使用pip指南
python·conda·pip
CodeArtisanX42 分钟前
高效管理 TensorFlow 2 GPU 显存的实用指南
人工智能·python·tensorflow
__Watson__1 小时前
【django项目使用easycython编译】Cannot convert Unicode string to ‘str‘ implicitly.
python·django
谢欣燕1 小时前
基于轨迹信息的图像近距离可行驶区域方案验证
python·opencv·计算机视觉