【问题标题】:When to write headers for a streaming response?何时为流式响应编写标头?
【发布时间】:2019-07-11 00:05:05
【问题描述】:

我正在使用 node-mssql 模块从 express api 查询 SQL 服务器,并将行(带有一些中间步骤)传送到响应流。我想避免写入标头,直到我知道查询不会在任何行之前产生错误,这样我就可以向客户端发送有用的错误消息,而不是使用 express 默认处理程序终止流。但是,express 在某些时候会编写默认标头,我无法识别。

我已尝试在管道中的 .once('data',) 侦听器中编写标头,但此时某些默认行为已为我编写标头,这会产生错误。

let request = await new sql.Request(pool);

res.writeHead(200, {
        'Transfer-Encoding': 'chunked',
        'charset' : 'utf-8',
        'Content-Type': 'application/json',
        'Content-Encoding': 'gzip'        
    })

ndjsonStream.on('error', next)

request.pipe(ndjsonStream)
    .pipe(transformer)
    .pipe(gzip)
    .pipe(res);

上述方法有效,但我无法传递有用的错误消息。

let request = await new sql.Request(pool);

ndjsonStream.once('data', () => {
    res.writeHead(200, {
        'Transfer-Encoding': 'chunked',
        'charset' : 'utf-8',
        'Content-Type': 'application/json',
        'Content-Encoding': 'gzip'        
    })
})

ndjsonStream.on('error', next)

request.pipe(ndjsonStream)
    .pipe(transformer)
    .pipe(gzip)
    .pipe(res);

request.query(query);

这会产生错误,因为 express 在此侦听器触发之前为我编写了一些标题,即 content-type: text/html。

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

【问题讨论】:

  • 除非将所有内容缓冲到内存中,否则无法确保在响应开始流式传输到客户端后不会发生错误。流式传输期间随时可能发生错误。标头必须在响应正文之前发送,因此 express 可能会在开始写入响应数据时发送一些默认标头。您可以尝试使用res.setHeader 强制它使用某种内容类型,但在开始将数据传送到响应之前需要进行错误检测。

标签: node.js express chunked-encoding node-mssql


【解决方案1】:

http 响应的工作方式是,您必须在开始发送任何数据之前编写 http 标头。这就是http响应格式的设计方式。因此,如果您要通过管道传输响应,则必须编写标头,然后通过管道传输并注意错误。您无法更改该顺序。

我想避免写标题,直到我知道查询不会在任何行之前产生错误

这样做的唯一方法是将整个响应预加载到内存中(这样您就知道在获取所有数据时没有发生错误),然后您可以编写标头,然后写入响应(如果没有错误)和如果出现错误,请编写不同的标头和不同的响应。

如果您是管道,那么根据定义,标头已经被写入,并且数据可能在错误发生之前已经开始发送。这是管道的一个缺点,就像您正在使用它一样。它有一些优点(比如可能会降低内存使用率和非常简单的编码),但是这样做确实没有很好的方法来处理错误。无需尝试真正非常喜欢多个 mime 部分,并允许您附加最终的 mime 部分以传达有关错误的更多信息并创建自己的管道解决方案,该解决方案不仅会在错误时挂断套接字,而且还允许您要终止当前的 mime 部分并添加另一个部分,如果您使用管道,则表示您接受它提供的简单错误通信。

【讨论】:

  • 感谢您的详细回答。根据回复,我认为我在解释我正在尝试做的事情方面做得很差。我试图处理的情况是在发出任何行之前出现错误。因此,例如,如果列名错误,我希望能够以有用的错误消息进行响应。在我知道查询将返回一些行之前,我试图避免编写标题。我给出的代码示例显示了在发出第一行但失败时写入标题的尝试。
  • @SurplusYogurt - 您将不得不手动执行流式传输的第一部分,然后您才能看到在调用 .pipe() 之前有一些行,因为 .pipe() 太自动了去做。我对你的 sql 流了解不够,不知道该怎么做。
猜你喜欢
  • 1970-01-01
  • 2021-08-30
  • 2021-11-16
  • 2015-10-17
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 2012-11-12
  • 2016-04-26
相关资源
最近更新 更多