Flask 400 错误排查:正确使用 FormData 发送 POST 数据

flask 接收 xmlhttprequest 的 post 请求时返回 400 错误,通常是因为前端未按 `application/x-www-form-urlencoded` 格式提交数据,而 flask 的 `request.form` 仅解析该格式;本文详解如何用 `formdata` 正确提交 json 字符串并避免 400 错误。

在 Flask 中,request.form 仅能解析符合 application/x-www-form-urlencoded(即表单编码)或 multipart/form-data 格式的请求体。而原始 JavaScript 代码中使用了:

xmlhttpPost.send("json=" + encodeURI(JSON.stringify(newJson)));

该写法虽构造了类似查询字符串的文本,但未设置正确的 Content-Type 头(默认为 text/plain),导致 Flask 无法识别并解析为 form 数据,调用 request.form["json"] 时触发 400 Bad Request(因键不存在或解析失败)。

✅ 正确做法是使用 FormData 对象 —— 它会自动设置 Content-Type: multipart/form-data; boundary=...(或在无文件时退化为 application/x-www-form-urlencoded),并确保字段被 Flask 正确捕获。

以下是修复后的完整前端逻辑(含注释与健壮性优化):

function del(e) {
    const getReq = new XMLHttpRequest();
    getReq.onload = function () {
        if (getReq.status !== 200) {
            console.error("Failed to fetch JSON:", getReq.statusText);
            return;
        }
        try {
            const json = JSON.parse(getReq.responseText);
            const newJson = json.filter(item => 
                item.name !== e.parentElement?.childNodes[2]?.textContent?.trim()
            );

            const postReq = new XMLHttpRequest();
            postReq.open("POST", "/orders");
            postReq.onload = function () {
                if (postReq.status === 200) {
                    location.reload(); // 或更新 UI
                } else {
                    console.error("POST failed:", postReq.statusText);
                }
            };

            const formData = new FormData();
            formData.append("json", JSON.stringify(newJson));
            postReq.send(formData); // ✅ 自动处理编码与 Content-Type

        } catch (err) {
            console.error("JSON parsing error:", err);
        }
    };
    getReq.open("GET", "/json");
    getReq.send();
}

同时,后端 Flask 路由也需增强容错性,避免对空值或缺失字段的直接访问:

from flask import Flask, request, render_template, jsonify
import json
from urllib.parse import unquote

@app.route("/orders", methods=["POST", "GET"])
def order():
    global orders
    if request.method == "GET":
        return render_template("orders.html", orders=orders)

    # ✅ 安全获取 form 数据:检查是否存在且非空
    if "json" not in request.form or not request.form["json"].strip():
        return "", 400  # Bad Request

    try:
        # ✅ 直接使用 request.form["json"](已确保存在),无需 unquote — FormData 发送时不额外编码
        orders = json.loads(request.form["json"])
        newOrder(orders)  # 假设此函数处理业务逻辑
        return "", 200
    except json.JSONDecodeError as e:
        return f"Invalid JSON: {e}", 400
    except Exception as e:
        return f"Server error: {e}", 500

⚠️ 关键注意事项:

  • 不要手动拼接查询字符串 + encodeURI:这无法触发 Flask 的 form 解析器;
  • 避免重复使用 xmlhttp 变量名:原代码中内层 const xmlhttp = ... 会遮蔽外层变量,引发作用域错误;
  • 始终校验 request.form 键存在性:直接访问 request.form["key"] 在键缺失时抛出 400;
  • 优先使用 fetch() 替代 XMLHttpRequest(现代推荐):
    fetch("/orders", {
        method: "POST",
        body: new FormData().append("json", JSON.stringify(newJson))
    });

通过 FormData 提交 + 后端健壮校验,即可彻底解决 Flask 400 错误,确保前后端数据交互稳定可靠。