【问题标题】: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() 的情况下工作,因为表单提交正常
  • 从链接的副本中,忽略已接受的答案并确保阅读其他答案

标签: javascript node.js mongodb express fetch


【解决方案1】:

这是因为您使用了错误的事件。

尝试在表单上使用 onsubmit 事件,而不是使用event.preventDefault(),在 onSubmit 函数中返回 false。

它看起来像这样:

<form onsubmit="onSubmit()">
  <button type="submit"></button>
</form>

然后是代码:

function onSubmit() {
  ... do your thing ...
  return false;
}

它应该可以工作,但如果没有,那么您将不得不自己将 formData 添加到正文中。 (如果需要,我会告诉你怎么做)。

更新

  1. 在表单中添加 id(您不必这样做,但它会让事情变得更容易。

    <form id="myForm">
      <button></button>
    </form>
    
  2. 获取表单数据:

    var formData = new FormData(document.getElementById('myForm'))
    
  3. 将它们作为请求正文传递给 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 属性。通常最好添加事件监听器
猜你喜欢
  • 2021-11-03
  • 2021-12-10
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 2020-09-21
  • 1970-01-01
  • 2021-04-08
  • 2019-07-22
相关资源
最近更新 更多