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
相关产品推荐
相关产品推荐

