【问题标题】:Error: Can't set headers after they are sent for HTTP node server错误:在为 HTTP 节点服务器发送标头后无法设置标头
【发布时间】:2018-11-28 21:16:43
【问题描述】:

我正在尝试根据 url 值将多个值传递给服务器。

因为我的结果值如下所示

[ { url: 'account/41',

    status: '200',

    headers:

     [ 'content-type = application/json,',

       'content-type = application/text' ],

    body: [ '{ name: XYZ }' ] },

  { url: 'account/43',

    status: '201',

    headers: [ 'content-type = application/xml']

    body: [ '{ name : ZYX }' ] } ]

在这里,我正在尝试为以上多个值创建服务器请求/响应。我使用 forEach 循环进行迭代,例如 if url = account/41 then body 将返回 [ '{ name: XYZ }' ] },如果 url = account/43 then body 将返回 ['{ name : ZYX }' ]。但它只取结果值的第一个元素并给我以下错误:

UnhandledPromiseRejectionWarning: Error: Can't set headers after they are sent.

我错过了什么但不确定是什么?如何通过正确的输出逐个传递值。

const http = require('http')

const parse = require('./Parse.js')



// create a server object:

let server = http.createServer(function(request, response) {

    parse.allFiles().then( results => {

        results.forEach(element => {

            if (element.url.includes(request.url) ) {

            let headerSplit = ( element.headers + '' ).split('=')

            let headername = headerSplit[0]

            let headerVal = headerSplit[1]

            response.setHeader( headername, headerVal )

            response.write( JSON.stringify( element.body ) )

            response.statuscode = parseInt( element.status )

            response.end()

        } else {

            response.writeHead(404, {'Content-Type': 'text/html'})

            response.end('No Page Found')

        }

        })

    })

})



const port = process.env.PORT || 8080



server.listen(port, function() {

// eslint-disable-next-line no-console

console.log('Listening on port ' + port)

})

我有多个文件,其中包含自己的 URL、STATUS、HEADERS 和 BODY。在单个数组中解析这些值(使用 Parse 函数)后,我将进入名为 results 的变量。

[{"url":"account/41","status":"200","headers":["content-type = application/json,"],"body":["{ name: xyz}"]},
{"url":"account/43","status":"201","headers":["content-type = application/xml"],"body":["{ name : zyx}"]}]

如果您遍历结果,您将获得类似 {"url":"account/41","status":"200","headers":["content-type = application/json"] 的元素, "body":["{ name: xyz}"]} 作为响应,如果您将 url 传递给服务器,例如 localhost:8080/account/41 ,它将返回 ["{ name : zyx}"]}] 作为响应身体。

【问题讨论】:

    标签: javascript node.js http


    【解决方案1】:

    .forEach() 循环中有一个 response.end()。这意味着您要多次调用它。您只能为每个请求调用一次,因为当您调用它时,会写入标头,刷新所有写入缓冲区并结束请求(例如关闭套接字)。因此,您不能多次“结束”请求。由于您的results.forEach() 循环是同步的,看来您可以在循环完成后将response.end() 放在某处,因此当循环中的所有response.write() 调用都完成时,它只会被调用一次。

    您还必须修复循环中的其他内容,因为您似乎在循环中间的某个地方决定,如果循环中的某个项目不起作用,您将返回 404。我不知道你真的应该有什么逻辑。或许您应该跳过任何通过 if 测试的项目,如果没有匹配则只发送 404?

    您还尝试在循环内一遍又一遍地设置状态和特定标题。那些只能有一个值,所以不清楚为什么你在循环中一遍又一遍地设置它。只有循环的最后一次迭代会坚持下去。

    因此,您关于如何处理循环的逻辑确实存在缺陷。我们必须更多地了解数据的外观以及您希望响应的外观,以便我们知道如何重新设计循环以使其正常工作。

    要修复的问题列表:

    1. 每个请求只能调用一次response.end()
    2. response.statuscode 只能有一个值,因此不清楚为什么要在循环中多次调用它。
    3. 您决定循环内的 404 状态,但随后循环继续尝试写入其他值。这种设计真的没有意义。也许您需要在遍历循环后根据您在循环中找到的内容来决定是否应该发送 404。
    4. 您的服务器会针对它收到的每个请求调用此代码。希望这只是因为您在发布时剥离了一堆代码,因为您永远不应该这样做。您应该有一个正在检查的特定路径,并且仅在请求该路径时才执行所有这些工作。否则,当爬虫探测您的网站、浏览器要求网站图标等时,您将完成所有这些工作......

    您还没有真正解释您想要构建什么响应,但我会猜测一下。如果您找到element.url.includes(request.url),那么您想要发送该响应并停止循环并完成。如果你在results 的任何一个中都没有找到element.url.includes(request.url),那么你想发送一个404。如果是这样,你可以这样做:

    const server = http.createServer(function(request, response) {
        parse.allFiles().then(results => {
            // find first element.url that matches our request.url
            const match = results.find(element => element.url.includes(request.url));
            if (match) {
                let headerSplit = (match.headers + '').split('=');
                let headername = headerSplit[0];
                let headerVal = headerSplit[1];
                response.setHeader(headername, headerVal);
                response.write(JSON.stringify(match.body));
                response.statuscode = parseInt(match.status);
                response.end()
            } else {
                response.writeHead(404, {
                    'Content-Type': 'text/plain'
                })
                response.end('No Page Found')
            }
        });
    });
    

    【讨论】:

    • 我有多个文件,其中包含自己的 URL、STATUS、HEADERS 和 BODY。在单个数组中解析这些值后,我得到了名为 results 的变量。 [{"url":"account/41","status":"200","headers":["content-type = application/json,","content-type = application/text"],"body" :["{ name: xyz}"]},{"url":"account/43","status":"201","headers":["content-type = application/xml","Accept = application /xml"],"body":["{ name : zyx}"]}].
    • @FatmaZaman - 为了让我更具体地提供帮助,您必须(在您的问题中)显示 results 数组中的内容以及您希望该数据的 http 响应是什么。我真的不知道您要创建哪种类型的响应,因此无法具体建议如何编写该响应。您可以发送一个数据数组,但您必须在一个响应中发送该数组,而不是 N 个响应。
    • 如果您遍历结果,您将一一获取元素,例如 {"url":"account/41","status":"200","headers":["content- type = application/json,","content-type = application/text"],"body":["{ name: xyz}"]}。然后如果你的 url 是 account/41 那么 body 值就是 ["{ name: xyz}].
    • results= [{"url":"account/41","status":"200","headers":["content-type = application/json,","content-type = application/text"],"body":["{ name: xyz }"]},{"url":"account/43","status":"201","headers":["content-type = application/xml","Accept = application/xml"],"body":["{ name :zyx}"]}]
    • @FatmaZaman - 多行代码在 cmets 中不可读。您应该编辑您的问题以将这种详细程度添加到问题中,然后将其适当地格式化为代码。重点是让您的问题更清晰,以便我们准确了解您拥有的数据类型以及您想要发送的响应类型。另外,您仍然没有显示您想要发送什么样的回复?
    猜你喜欢
    • 1970-01-01
    • 2016-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-27
    • 2011-09-13
    • 2011-02-21
    • 2023-03-08
    相关资源
    最近更新 更多