【问题标题】:Getting "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" when trying to redirect尝试重定向时出现“错误 [ERR_HTTP_HEADERS_SENT]:在将标头发送到客户端后无法设置标头”
【发布时间】:2020-05-25 08:28:43
【问题描述】:

我正在尝试创建一个包含数据库 (mongodb) 的简单服务器。我试图通过制作一个带有 2 个输入和一个发送按钮的简单表单来检查是否可以将数据插入其中:

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Test</h1>
    <p>Test</p>

    <form action="/attack-data" method="POST">
        <input type="text" placeholder="name" name="name">
        <input type="text" placeholder="quote" name="quote">
        <button type="submit">Submit</button>
      </form>

</body>
</html>

然后我尝试在后端捕获发送的数据,然后再次重定向到主页。可悲的是,当我这样做时,我得到了这个错误:

(node:11508) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:526:11)
    at ServerResponse.header (C:\Users\lenovo\Documents\Project\node_modules\express\lib\response.js:771:10)
    at ServerResponse.json (C:\Users\lenovo\Documents\Project\node_modules\express\lib\response.js:264:10)
    at C:\Users\lenovo\Documents\Project\Server.js:47:41
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:11508) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error 
originated either by throwing inside of an async function without a catch block, or by 
rejecting a promise which was not handled with .catch(). To terminate the node process 
on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)       
(node:11508) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

我对这个问题感到不满,我知道它发生在服务器不止一次响应客户端时,我的服务器第二次响应是当我尝试重定向回发布请求内的主页时。但我还没有找到我发送第一个回复的地方。

后端:

let express = require('express');
let mongo = require('mongodb').MongoClient;
let bodyParser = require('body-parser');

let url = "mongodb://localhost:27017/database";
let port = 9999;
let app = express();

app.use(express.json());
app.use(bodyParser.urlencoded({extended: true}));

// Connecting to the database (The database is created if it doesn't exist)
mongo.connect(url, {useNewUrlParser:true, useUnifiedTopology:true}, function(error, client)
{
    if (error)
    {
        console.log("Error: Couldn't Create/Connect The Database");
        throw error;
    }
    console.log("Connection To The Database Has Been Established Successfully");
    const database = client.db('attack-patterns');
    const attackCollection = database.collection('temp');

    app.get('/', function(request, response)
    {
        response.sendFile(__dirname + '/public/index.html');
    })

    app.post('/attack-data', function(request, response)
    {
        console.log(request.body);
        attackCollection.insertOne(request.body)
        .then(function(result)
        {
            console.log(result);
            return response.redirect('/');
        })
        .catch(function(error)
        {
            console.log("Error: Couldn't Insert Data To Database: " + error);
            return response.status(404).json({error});
        })
        response.end();
    });

    client.close();
});


// Listening to the port saved in the variable "port"
app.listen(port, () => console.log("Server Is Listening To Port: " + port));

谢谢!

【问题讨论】:

  • 这是因为您在数据库操作完成之前立即运行response.end()

标签: javascript node.js mongodb


【解决方案1】:

/attack-data 路由中,您在attackCollection.insertOne 上启动了一个承诺,并且您希望在.then.catch 中发送响应 但是当启动一个promise时,后面的代码会同时执行(在启动promise 一个线程)所以你的response.end() 在“.then”之前执行

您只需删除response.end(),您的代码就可以工作了!

【讨论】:

  • 哦,我现在明白了,非常感谢!
  • 我想补充一点:“在线程中启动 promise”是不正确的,promise 的回调被推送到微任务队列中——一旦准备好就会被执行。不涉及第二个线程 -> javascript.info/event-loop
【解决方案2】:

response.end(); 在您的 insertOne 承诺解决之前执行,因此一旦处理程序 (then/catch) 执行,响应已经结束,即标头已经发送。这就是您看到上述错误的原因。

您可以简单地删除 response.end 语句,因为 response.redirect() and response.json() 已经处理发送响应。

此外,您不应在 mongo-connect 回调中声明路由处理程序,而应在其外部定义它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-26
    • 2018-09-19
    • 2020-09-22
    • 2023-01-19
    • 2021-02-26
    • 2021-08-20
    • 2019-09-28
    相关资源
    最近更新 更多