当我将 event.preventDefault() 与 fetch api、express 和 node.js 一起使用时,为啥 req.body 返回 null? [复制]
Posted
技术标签:
【中文标题】当我将 event.preventDefault() 与 fetch api、express 和 node.js 一起使用时,为啥 req.body 返回 null? [复制]【英文标题】:Why does req.body return null when I was use event.preventDefault() with fetch api, express, and node.js? [duplicate]当我将 event.preventDefault() 与 fetch api、express 和 node.js 一起使用时,为什么 req.body 返回 null? [复制] 【发布时间】:2020-06-27 02:30:34 【问题描述】:我想提交一个表单,然后从我的 MongoDB 数据库中返回一个对象,而无需重新加载页面。因此,当我提交表单时,我使用了一个事件处理程序来阻止表单的默认行为重新加载页面。问题是这干扰了我的服务器端脚本并导致req.body
返回null。如果我从客户端脚本中删除e.preventDefault()
,那么req.body
工作正常,但页面会刷新并仅显示数据库中的 json。我该如何解决这个问题?
这是我的 html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>Message in a Bottle</title>
<link href="https://fonts.googleapis.com/css?family=Roboto+Slab&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div id = "icon">
<img id = "iconImg" style = "width:100%"src = "icon.svg"/>
</div>
<div id = "form">
<form method="post" action="/">
<input type="text" name="sender"/>
<textarea rows="20" cols="50" name="message"></textarea>
<button type = "submit" onclick="onClick(event)">submit</button>
</form>
</div>
<div id = "message">
<p id = "senderName"></p>
<p id = "response"></p>
<button onclick="closeMessage()">New Message</button>
</div>
</body>
<script type = "text/javascript" src = "packageHandler.js"></script>
</html>
这是我的客户端脚本:
const form = document.getElementById("form");
const icon = document.getElementById("icon");
const message = document.getElementById("message");
function onClick(e)
e.preventDefault();
console.log("loading results");
form.style.opacity = "0%";
form.style.pointerEvents = "none";
icon.style.width = "80%";
fetch('http://localhost:3000/', method: 'POST')
.then((response) =>
icon.style.width = "33%";
message.style.opacity = "100%";
message.style.pointerEvents = "auto";
return response.json();
)
.then((data) =>
document.getElementById("senderName").innerHTML = data.name;
document.getElementById("response").innerHTML = data.message;
);
//await results
//fade in <div id = "message">
//display results in <div id = "message">
function closeMessage()
console.log("message closed")
form.style.opacity = "100%";
form.style.pointerEvents = "auto";
message.style.opacity = "0%";
message.style.pointerEvents = "none";
//fade out <div id = "message">
//fade in <div id = "form"
这是我的服务器端脚本:
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
require('dotenv/config');
const app = express();
const fs = require('fs');
const port = 3000;
app.use(express.urlencoded());
app.use(express.static('public'));
app.listen(port, () => console.log(`Example app listening on port $port!`));
//Routes
app.get('/', (req, res) =>
fs.readFile('./views/home.html', function (err, data)
res.writeHead(200, 'Content-Type': 'text/html' );
res.write(data);
res.end();
);
);
app.post('/', (req, res) =>
MongoClient.connect(process.env.DB_CONNECTION, useUnifiedTopology: true, useNewUrlParser: true , function (err, db)
if (err) throw err;
const dbo = db.db("mydb");
const messageTable = dbo.collection("messages");
let myobj =
[
name: req.body.sender,
message: req.body.message
];
console.log(myobj)
messageTable.insertMany(myobj, function (err, res)
if (err) throw err;
console.log("1 document inserted");
);
var myPromise = () =>
return new Promise((resolve, reject) =>
messageTable.aggregate(
[ $sample: size: 1 ]
).toArray((err, data) =>
err
? reject(err)
: resolve(data[0]);
);
);
//Step 2: async promise handler
var callMyPromise = async () =>
var result = await (myPromise());
//anything here is executed after result is resolved
return result;
;
//Step 3: make the call
callMyPromise().then(function (result)
db.close();
res.json(result)
);
);
); //end mongo client
【问题讨论】:
您的fetch
呼叫缺少请求body
@Phil 你介意详细说明一下吗?
@Phil 善待并阅读。当他不使用 event.preventDefault()
时它会起作用,所以浏览器可能有一些预定义的行为将 formData 添加到它自己的正文中。
@PavelB。它可以在没有preventDefault()
的情况下工作,因为表单提交正常
从链接的副本中,忽略已接受的答案并确保阅读其他答案
【参考方案1】:
这是因为您使用了错误的事件。
尝试在表单上使用 onsubmit 事件,而不是使用event.preventDefault()
,在 onSubmit 函数中返回 false。
它看起来像这样:
<form onsubmit="onSubmit()">
<button type="submit"></button>
</form>
然后是代码:
function onSubmit()
... do your thing ...
return false;
它应该可以工作,但如果没有,那么您将不得不自己将 formData 添加到正文中。 (如果需要,我会告诉你怎么做)。
更新
在表单中添加 id(您不必这样做,但它会让事情变得更容易。
<form id="myForm">
<button></button>
</form>
获取表单数据:
var formData = new FormData(document.getElementById('myForm'))
将它们作为请求正文传递给 fetch。
fetch('url',
method: 'POST',
headers:
'Content-Type': 'multipart/form-data'
,
body: formData
)
【讨论】:
我尝试了你的建议,现在它又重新加载了。 @TDonnally 好的,那么你必须努力做到这一点。给我一秒钟 OP 没有 JSON 请求正文解析器,因此您不应该发布 JSON。此外,您不能JSON.stringify()
FormData
实例
另外,如果您使用onsubmit
属性,则需要使用onsubmit="return onSubmit()"
来阻止默认事件
@Phil 谢谢,我不正常使用 onsubmit。我像其他假人一样使用点击事件,而且大多数时候我什至不使用表单标签。【参考方案2】:
当您使用fetch
提出请求时,表单中的数据不包括在内。
要包含数据,您必须手动收集数据并将其与请求一起发送。
超级懒惰的例子:
编辑:根据this answer更改为FormData
编辑 2:FormData 以multipart/form-data
发送,因此我们需要先对其进行 url 编码。 (感谢@Phil)
var forms = document.querySelectorAll('form');
forms.forEach((form) => form.onsubmit = onsubmit);
function onsubmit(e)
e.preventDefault();
var form = e.target;
var formData = new FormData(form);
var encodedData = new URLSearchParams(formData).toString()
// log form values
console.log(encodedData);
fetch('/',
method: 'POST',
headers:
'Content-Type': 'application/x-www-form-urlencoded'
,
body: encodedData
)
.then(result =>
);
<form method="post" action="/">
<input type="text" name="sender"/>
<textarea rows="20" cols="50" name="message"></textarea>
<button type = "submit">submit</button>
</form>
【讨论】:
OP 需要添加app.use(express.json())
才能正常工作。此外,您提到了 Content-type
标头,但您没有在请求中添加任何标头
关于通过fetch
提交FormData
实例的有趣事实;这将发布multipart/form-data
请求正文
还有第二个有趣的事实。 FormData 类非常适合发送图像或文件。
另一个(实际上)有趣的事实,如果您将请求正文设置为URLSearchParams
的实例(而不是字符串),则内容类型将默认为application/x-www-form-urlencoded
,因此您可以省略headers
部分(也是headers
,而不是header
)
最后,我不推荐使用onsubmit
属性。通常最好添加事件监听器以上是关于当我将 event.preventDefault() 与 fetch api、express 和 node.js 一起使用时,为啥 req.body 返回 null? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
Javascript event.preventDefault() 不适用于模态
$('.classitem').submit(function(event)event.preventDefault) 不起作用