You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Fetch发送POST请求未携带Body,request.get_json返回空值

React请求云函数POST时后端无法获取Body的原因分析

我开发了一个React自定义组件,用于向云函数HTTP地址发送POST请求,对应的curl命令如下:

curl --location --request POST 'https://CLOUD-FUNCTION-HTTP-URL' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer foo' \
--header 'Accept: application/json' \
--data-raw '{
    "message": "hello"
}'

使用Postman发送该请求时,后端能正常接收Body内容;但通过React应用发送时,后端无法获取到Body(request.get_json(silent=True)返回空值)。后端实现参考了谷歌云函数的两个教程:将CORS示例中的GET替换为POST,以及HTTP内容处理示例。

React组件代码如下:

import * as React from "react";
import { useState } from "react";
import CircularProgress from "@mui/material/CircularProgress";
import { Button, TextField } from "@mui/material";
import { getBearerToken } from "../auth/firebase/AuthProvider";

export default function Wrapper() {
  const [prompt, setPrompt] = useState("Do something");
  const [data, setData] = useState([]);
  const [status, setStatus] = useState(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    setStatus("loading");

    getBearerToken().then(
      function (token) {success(token);},
      function (error) {setStatus("error");}
    );

    function success(token) {
      const requestOptions = {
        method: "POST",
        headers: {
          "Accept": "application/json",
          "Content-Type": "application/json",
          "Authorization": "Bearer " + token
        },
        body: JSON.stringify({
          "message": "hello"
        }),
      }

      fetch("https://CLOUD-FUNCTION-HTTP-URL", requestOptions)
        .then((res) => res.json())
        .then((data) => {
          console.log(data);
          setData(data);
          setStatus("success");
        })
        .catch((err) => {
          console.log(err)
          console.log(err.message);
          setStatus("error");
        });
    }
  };

  function handleSetPrompt(e) {
    setPrompt(e.target.value);
  }

  return (
    <>
      <form onSubmit={handleSubmit}>
        <TextField
          id="standard-basic"
          label="Standard"
          variant="standard"
          onChange={handleSetPrompt}
          value={prompt}
        />
        <Button color="inherit" type="submit">Fetch</Button>
        <LoadingSpinner shouldHide={status !== "loading"} />
      </form>
      <div>
        <p>{prompt}</p>
        <p>{status}</p>
      </div>
    </>
  );
}

const LoadingSpinner = (props) => (
  <div className={props.shouldHide ? "hidden" : undefined}>
    <CircularProgress></CircularProgress>
  </div>
);

导致差异的核心原因

1. 跨域预检请求(OPTIONS)的影响

浏览器环境下的React应用发送跨域POST请求时,会自动先发送OPTIONS预检请求,验证目标服务器是否允许跨域请求。而Postman、curl这类工具不会触发预检请求,直接发送POST。
如果云函数的CORS处理逻辑存在问题:

  • 比如在处理OPTIONS请求时错误读取了请求体(但OPTIONS本身没有Body),会导致后续实际POST请求的Body被提前消耗(WSGI规范中请求体只能被读取一次);
  • 或者OPTIONS响应头配置不正确(比如未允许Content-Type、Authorization等自定义头),浏览器会拦截后续的POST请求,导致Body根本没传到后端。

2. 云函数中请求体被提前读取

如果云函数的代码在调用request.get_json()之前,已经通过request.data、request.get_data()等方式读取过请求体,那么request.get_json()会返回空值。Postman/curl的请求可能因为没有预检流程,代码执行路径不会触发提前读取;但React的请求因为有预检,可能在处理OPTIONS时不小心执行了读取请求体的逻辑,导致后续POST请求的Body无法被解析。

解决方案建议

  • 修正云函数的CORS处理:确保OPTIONS请求仅返回必要的响应头(如Access-Control-Allow-Origin、Access-Control-Allow-Methods: POST, OPTIONS、Access-Control-Allow-Headers: Content-Type, Authorization),绝对不要在OPTIONS请求中读取请求体。
  • 检查云函数POST处理代码:确保在调用request.get_json()之前,没有任何读取请求体的操作。
  • React端显式声明跨域模式:在fetch的requestOptions中添加mode: 'cors'(虽然默认是cors,但显式设置可以避免潜在的浏览器兼容问题)。

内容的提问来源于stack exchange,提问作者WJA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 17:16:07