解决JS Fetch API向FastAPI发送POST请求的CORS和400/422错误
2025-01-02 19:19:37
使用 JavaScript Fetch API 向 FastAPI 应用发送 POST 请求
在开发过程中,常常需要在前端使用 JavaScript 代码与后端 FastAPI 应用进行交互。一个常见的情况是,你需要发送 POST
请求传递数据给 FastAPI 接口。这期间可能会遇到一些问题,特别是跨域资源共享 (CORS) 和请求数据格式错误等问题。本文将深入探讨这些问题的常见原因和相应的解决方案。
问题分析
从来看,报错信息显示了两个问题:
400 Bad Request
: 表示服务器理解请求,但是由于客户端的原因,服务器不会继续处理它422 Unprocessable Entity
: 表示服务器能够理解客户端的请求,但是请求参数类型有误,导致无法处理
问题根源在于以下几点:
- CORS 问题 : 浏览器的安全机制会限制跨域请求,如果你的 HTML 页面和 FastAPI 应用不在同一个域,就需要配置 CORS,否则请求可能会被浏览器拦截。你的 FastAPI 代码里通过设置
origins=["*"]
的方式来允许所有的域,看起来像是没有问题的。但它有可能仍然没有正常生效。 - 请求格式错误 :FastAPI 需要请求体以 JSON 格式发送,并且数据结构需要和后端声明的
pydantic
模型匹配,前端代码有可能请求体不是JSON或者数据格式不正确。 - Preflight Request (OPTIONS): CORS 请求首先会发起一个
OPTIONS
请求,询问服务端是否允许接下来的实际请求, 如果服务端没有处理,则会报400
错误。
解决方案
接下来,我们将分步介绍如何解决这些问题。
方案一:配置正确的 CORS 中间件
尽管你的代码设置 origins=["*"]
允许所有域,但这还不足以确保所有的跨域场景都能工作。确保中间件被正确地添加。 某些情况下,浏览器仍会发送OPTIONS
预检请求, 如果FastAPI没有正确的处理, 会报400错误。建议直接把FastAPI应用服务器设成可被所有的来源访问的服务器,代码如下。同时建议指定方法(allow_methods
)以及headers(allow_headers
),可以有效防止后续出现的一些安全问题:
FastAPI 代码:
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许所有来源
allow_credentials=True,
allow_methods=["*"], # 允许所有请求方法
allow_headers=["*"], # 允许所有请求头
)
# 其他代码保持不变
修改之后, 需要重启FastAPI 服务器使改动生效。这个改动会放宽服务器对跨域的限制。
方案二:发送正确的 POST 请求
请使用 Fetch API 或者 axios 来发送 POST
请求。并且确保请求体的格式是 JSON
,并设置 Content-Type
为 application/json
。使用jquery的话注意观察版本号,老版本可能对ajax post的处理有所区别。
HTML 代码 (使用 fetch):
<!DOCTYPE html>
<html>
<body>
<br><br><br><span style="text-align: center;"><button id="send">Send Request</button></span>
</body>
<script>
const sendButton = document.getElementById("send");
sendButton.addEventListener('click', () => {
const url = "http://localhost:8000/add_points/";
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({"name":"John", "points":50.0})
}).then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // 成功请求
})
.then(data => {
alert("Data: " + data);
console.log('Data:', data);
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error.message);
});
});
</script>
</html>
使用 fetch 的步骤:
- 创建一个
fetch
调用,传入 URL 以及配置对象。 - 在配置对象中,设置请求方法为
'POST'
,添加headers
,确保Content-Type
是application/json
。 - 使用
JSON.stringify
把请求体转换成 JSON 字符串。 - 处理响应, 使用
response.ok
确保返回的请求成功,使用response.json()
来获取后端返回的 JSON 结果,错误捕获应该放在.catch
代码块中。
方案三:检查 FastAPI 请求参数结构
确认前端发送的数据格式是否和 FastAPI 中 AddTransaction
模型匹配。 AddTransaction
模型有name和points这两个参数, 并且需要是json
格式.确保你在 JavaScript 代码中发送的请求体,和你的Pydantic
的模型完全一致,以下列出常见的请求body和预期模型的示例。
- 错误格式
{"name": "string", "point":5.0}
上面JSON和 AddTransaction
模型的属性不一致,应该叫 points
, 且需要保持名称一致性。
- 正确格式
{"name": "John", "points": 5.0}
{
"name": "string",
"points": 5.0
}
- 如果服务端
Pydantic
模型接受的类型是float
而不是字符串,那么确保points
的值是数字类型,而不是字符串形式的数值。 User
,Item
以及UserPoints
遵循类似的逻辑
其他要点:
- 错误处理: JavaScript 代码中应包含错误处理机制(例如
fetch.catch
代码块),以便及时发现问题。 - 安全: 允许
origins=["*"]
方便本地调试,在生产环境中需精确设置允许的域名。 - 日志: FastAPI 日志可以帮助调试,注意查看报错信息,以此排查后端逻辑问题
总结
要解决使用 JavaScript Fetch API 发送 POST 请求给 FastAPI 应用的问题,需要正确配置 CORS,确保发送的请求体格式正确,并检查前后端数据结构的匹配。在调试过程中,仔细查看错误信息并运用浏览器开发者工具是关键。遵循这些步骤,你应该能够顺利实现前后端数据的交互。