【问题标题】:Error: Can't set headers after they are sent. error received after refreshing a get request错误:发送后无法设置标头。刷新获取请求后收到错误
【发布时间】:2018-07-08 21:23:41
【问题描述】:

以下代码在第一次执行时按预期工作。第二次通过它失败并出现以下错误:
Error: Can't set headers after they are sent.
基本上,我试图从一系列已发布的 mqtt 主题中返回一条消息。我已经尝试了几种方法来做到这一点,但对这种行为感到茫然。下面的代码显示了使用 this MDN 文章中详述的 Promise 的尝试。我也尝试过使用 async / await(如果需要,我也可以发布此代码)。

概念
get 请求从网页到达,这会触发将 mqtt 消息发布到本地代理。一个单独的端点正在侦听此主题。在这种情况下,一个带有连接网络摄像头的树莓派。主题被接收到 rpi 做它的事情并发布带有 url 作为它的数据的返回消息。初始 mqtt 发布者正在侦听此响应并触发 res.json() 作为响应。
这第一次有效,当我刷新网页时,它在发送后失败并显示无法标头。如何重置资源?看来是这样的。

punchitRoute.js

const express = require('express');
const punchitRoute = express.Router();
const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://192.168.88.54:1883');

client.subscribe('returned', function(){
    console.log('subscribed');
})

async function img(){
    return new Promise(resolve =>{ client.on('message', function(topic, url){
            const message = {
                url: url.toString(),
                name: "garfield",
                date: Date.now()
            }
        return message;
        })
     })

}

 punchitRoute.get('/', async function(req, res){
    const mess = await img();
    console.log(mess);
    res.json(mess);
});

module.exports = punchitRoute;

server.js

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const punchit = require('./routes/punchitRoute');


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

app.get("/", (req, res) => {

    res.json({
      name: "Express application",
      message: "welcome to punchit"
    })
  });

app.use('/api/punchit', punchit);

  const PORT = process.env.PORT || 6040;
  app.listen(PORT);
  console.log('server started on: ', PORT);

我的猜测是,我错过了关于使用 express 请求的基本生活事实。
提前感谢您的任何指导。

【问题讨论】:

  • 对于初学者来说,你的 img() 函数永远不会调用 resolve() 来解决它的承诺。
  • 其次,每次调用img() 时,都会为client.on('message', ...) 添加另一个事件处理程序。这些会堆积起来,你会有重复的,你会尝试多次回复相同的消息。通常,promise 不适合事件处理程序,因为如果多次调用函数会出现顺序问题,但如果您打算以这种方式使用它们,则必须在事件处理程序触发后删除它。即便如此,仍有问题需要解决。
  • 感谢您的回复。在这种情况下使用 async / await 会更好吗?
  • 老实说,您的代码太混乱了,我无法真正建议您应该做什么。您需要提供您想要完成的任务的完整描述(用文字而不是代码),以便我们可以从头开始建议最好的实现。 async/await 不会解决我评论过的任何问题。
  • confused 是一种很好的表达方式。上面的代码是错误的,我复制的文件是在没有测试的情况下编辑的,当我终于意识到我没有在测试中调用这个文件并切换它时,上面的代码在几个方面都失败了。我将重新发布对代码的编辑并重新描述目标。

标签: node.js express mqtt


【解决方案1】:

感谢 jfriend00 为我指明了正确的方向。首先,发布的代码完全不正确。我对此感到非常困惑,随后重命名了文件punchitRoute 文件以继续以不同的方法处理它,但忘记在 server.js 中重命名所需的文件!那是一个愚蠢的错误。

关于client.on('message', ...) 的事件处理程序的第二条评论给了我需要查看事件循环的提示。

这里的想法是使用来自 get 请求的 mqtt 从网络摄像头拍摄图像。 get 请求进入 express 服务器,触发对名为 'returned' 的主题的订阅,并发布到名为 'sunrise' 的主题 strong> 带有 'click' 的消息。

本地网络上有一个单独的端点,并正在侦听正在侦听 'sunrise' 主题的本地 mqtt 代理(均与 express 服务器共享) .在图像保存到 S3 存储桶后,此端点广播 'returned',并将图像的 url 作为消息发送回运行 get 请求的服务器。 mqtt 函数运行良好,我遇到的问题是响应事件没有在响应结束时终止。为了这也有效,有几件事必须改变。

  1. 将 res.send 移到一个在 client.on 之后调用的函数中(这并不完全奏效,它允许发送但仍然返回错误)
  2. 添加了事件发射器以查看反复触发的事件(这让我对正在发生的事情有了一些了解,但仍然没有消除错误)
  3. 研究了其他 mqtt 库(最终还是使用 async-mqtt)
  4. 在 res.send 函数之后为两个主题添加了 client.unsubscribe (这没有效果)
  5. 终于找到了this 的答案,这让我意识到我可以一直调用client.once!

这是获取请求的代码:

punchitRoute.get('/', async function(req, res){
//adding event listner 'send'
mqEvent.on('send', function(){
    console.log('added send');
});

let sendit = function(url){
    'send', //emmitting 'send'
    res.status(200).send(
        '<img src='+url.toString()+' />'+
        '<h1>PUNCHED!</h1>'+
        '<p>'+
        'name: garfield <br/> time: '+moment(Date.now()).tz("America/Louisville").format('MM-DD-YYYY'));
    }

client.subscribe('returned', function(){
    console.log('subscribed');
});
client.publish('sunrise', 'click');
//changed this to .once and no more errors
await client.once('message', async function callout(topic, url){
    try {

            mqEvent.on('message', function(){
                console.log('added message');
            });

        sendit(url.toString());

            // console.log(res);
        client.unsubscribe('returned', ()=>{
            console.log('unsubscribed');
        });
        client.unsubscribe('sunrise', ()=>{
            console.log('unsubscribed publish');
        });
        //clearing out the send event listeners
        mqEvent.removeAllListeners('send');
        console.log(mqEvent.listenerCount('send'));
        //clearing out the message event listeners
        mqEvent.removeAllListeners('message');

     } catch(e){
            console.log(e);
            console.log('error');
        }
    })
});

希望这可以让其他人面临类似的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-17
    • 1970-01-01
    • 2018-06-08
    • 2018-06-29
    • 2013-12-22
    • 1970-01-01
    相关资源
    最近更新 更多