如何正确取消Node.js中的http请求?

20

我需要在Node.js中实现可取消的客户端HTTP请求,不使用外部库。我会提供一个 Promise 对象 - cancellationPromise - 当请求被取消时该对象会被拒绝。这就是我知道何时需要调用 request.abort() 的方式。

问题是,我应该仅在 https.request 仍处于挂起状态且 response 对象尚不可用时才调用 request.abort() 吗?

或者,即使我已经得到了 response 对象并正在处理响应数据,我也应该调用它,就像下面的代码一样吗?在这种情况下,这是否会停止任何更多的 response.on('data') 事件?

  async simpleHttpRequest(url, oauthToken, cancellationPromise) {
    let cancelled = null;
    let oncancel = null;

    cancellationPromise.catch(error => { 
      cancelled = error; oncancel && oncancel(error) });

    try {
      const response = await new Promise((resolve, reject) => {
        const request = https.request(
          url.toString(), 
          { 
            method: 'GET', 
            headers: { 'Authorization': `Bearer ${oauthToken}` }        
          }, 
          resolve);

        oncancel = error => request.abort();
        request.on('error', reject);
        request.end();
      });

      if (cancelled) throw cancelled;

      // do I need "oncancel = null" here?
      // or should I still allow to call request.abort() while fetching the response's data?

      // oncancel = null;

      try {
        // read the response 
        const chunks = await new Promise((resolve, reject) => {
          response.on('error', reject);  
          const chunks = [];
          response.on('data', data => chunks.push(data));
          response.on('end', () => resolve(chunks));
        });

        if (cancelled) throw cancelled;
        const data = JSON.parse(chunks.join(''));
        return data;
      }
      finally {
        response.resume();
      }
    }
    finally {
      oncancel = null;
    }
  }

1
你应该只在收到响应之前中止请求。一旦你收到响应,你应该根据响应决定是否要中止请求或者想要成功的响应,而不是立即中止请求。 - Akansh
@AkanshGulati,如果在我仍在接收数据块时,cancellationPromise被拒绝了,那该怎么办? - avo
1个回答

21

这取决于您通过"aborting a request"想要实现什么目的。

一些背景知识:HTTP 1无法"取消"请求,一旦发送请求就必须等待响应。您不能"回滚"已发出的请求,需要使用分布式事务。(详细阅读。)正如MDN开发文档所述:

如果已经发送了请求,则XMLHttpRequest.abort()方法将中止该请求。当请求被中止时,它的readyState更改为XMLHttpRequest.UNSENT(0),请求的状态码设置为0。

基本上,您停止了由您的应用程序处理的响应。另一个应用程序可能会(如果您在发送请求后调用了abort())仍然完成其处理。

从问题的角度来看:

问题是,我是否只应在https.request仍处于挂起状态且响应对象尚不可用时调用request.abort()

TL;DR:这仅涉及您的应用程序的视角。浏览您的代码,我认为它将正常工作。


谢谢!不过我还是有点好奇,如果我已经得到了response对象并且仍然调用request.abort,你知道这是否会停止未决响应数据的即将到来的response.on('data')事件吗?我认为会,因为文档说:“标记请求为中止。调用此方法将导致响应中剩余的数据被丢弃并销毁套接字。”但我还没有自己验证过。此外,在这种情况下,我是否仍需要调用response.resume() - avo
1
据我所知,这并没有严格规定,但在我的过去的项目中,你所做的假设是正确的。也就是说,它停止了 response.on(data)。但我们已经删除了这些代码部分,因为对我们没有任何好处。 - Hash
4
可以在HTTP GET或HEAD时中止请求,但不能在POST或PUT中止。一旦调用者决定中止,他们可能不希望调用任何钩子函数或让承诺被拒绝,因此建议永远中止请求。 - root
1
我在某种程度上同意你的观点。大多数情况下,HEADGET不会更改数据。可悲的是,我见过一些系统违反了这个规则... - Hash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接