
使用 fetch 发送 post 请求时,因未正确处理 cors 预检(preflight)机制,浏览器自动发起 options 请求后又执行实际 post,导致 php 后端被调用两次,引发空数据与正常数据的双重插入。
使用 fetch 发送 post 请求时,因未正确处理 cors 预检(preflight)机制,浏览器自动发起 options 请求后又执行实际 post,导致 php 后端被调用两次,引发空数据与正常数据的双重插入。
这是一个典型的由 CORS 预检请求(Preflight Request) 引发的“意外双提交”问题。当你的 fetch 请求满足以下任一条件时,浏览器会自动在真正 POST 之前,先发送一个 OPTIONS 请求进行预检:
- 设置了自定义请求头(如
'Content-Type': 'application/json'); - 使用了除
GET/POST/HEAD外的方法; -
Content-Type值不是application/x-www-form-urlencoded、multipart/form-data或text/plain。
在你原始代码中,虽然注释掉了 headers,但关键点在于:只要 headers 对象存在(即使为空)或显式设置了非简单 Content-Type,就可能触发预检。而你的 PHP 脚本未对 OPTIONS 请求做任何拦截或响应,导致:
- 第一次
OPTIONS请求到达时,PHP 执行完整逻辑(file_get_contents('php://input')读取为空 →$data为null或空数组 → 插入默认/空值); - 第二次真正的
POST请求到达,才插入正确数据。
✅ 正确解决方案是 避免触发预检,即让请求成为“简单请求”:
-
移除
headers配置项(如你已发现); -
不显式设置
Content-Type,或仅使用text/plain(此时JSON.stringify()的字符串会被原样发送,PHP 需相应调整解析方式); - 或改用
FormData(天然为multipart/form-data,且无需额外 header)——但注意需服务端配合解析。
以下是优化后的 JavaScript 示例(推荐方案):
async function uploadImage() {
const url = "http://localhost/upload_image.php";
navigator.geolocation.getCurrentPosition(writePosition);
const dataJSON = {
"Koordinate_X": 3,
"Koordinate_Y": 1,
"Kategorie": 1,
"Bild": "Es klappt aus der App"
};
// ✅ 关键:不传 headers,不设 Content-Type → 触发简单请求
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(dataJSON) // 浏览器自动设 Content-Type: text/plain
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
}对应地,PHP 端需确保稳健解析:
⚠️ 注意:当 Content-Type 缺失或为 text/plain 时,php://input 仍可读取原始 JSON 字符串,但务必校验解码结果:
<?php
header('Access-Control-Allow-Origin: http://localhost:5173');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type'); // 允许预检中检查该头(即使不发送)
// ✅ 显式检查请求方法,跳过 OPTIONS
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
$input = file_get_contents('php://input');
$data = json_decode($input, true);
// ✅ 强制校验解码是否成功且数据结构完整
if (json_last_error() !== JSON_ERROR_NONE || !is_array($data) ||
!isset($data['Koordinate_X'], $data['Koordinate_Y'], $data['Kategorie'], $data['Bild'])) {
http_response_code(400);
die('Invalid or missing JSON payload');
}
// 后续数据库插入逻辑(保持不变)...
$database_path = $_SERVER["DOCUMENT_ROOT"] . "\WoIstMein.accdb";
if (!file_exists($database_path)) die("Access database file not found !");
try {
$database = new PDO("odbc:DRIVER={Microsoft Access Driver (*.mdb, *.accdb)}; DBQ=$database_path;");
$sql = 'INSERT INTO Gegenstaende (Koordinate_X, Koordinate_Y, Kategorie, Bild) VALUES (:koord_x,:koord_y,:kategorie,:bild)';
$stmt = $database->prepare($sql);
$stmt->execute([
':koord_x' => (int)$data["Koordinate_X"],
':koord_y' => (int)$data["Koordinate_Y"],
':kategorie' => (int)$data["Kategorie"],
':bild' => (string)$data["Bild"]
]);
} catch (PDOException $e) {
http_response_code(500);
error_log($e->getMessage());
die('Database error');
}
?>? 总结与最佳实践:
- 避免无意义的
headers配置;若必须发送 JSON,优先用Content-Type: application/json+ 完整 CORS 配置(含OPTIONS处理),而非妥协为text/plain; - 前端应添加
await和错误处理,防止静默失败; - 后端必须校验请求方法、输入格式与数据完整性,绝不可假设
php://input总有有效内容; - 开发时善用浏览器 DevTools 的 Network → Filter → XHR/Fetch 面板,观察是否出现
OPTIONS请求,这是诊断此类问题的第一步。

















