为什么axios请求错误会导致一个未处理的异常,即使所有的调用都以await开头,并被 Package 在try/catch块中?

mf98qq94  于 2022-11-23  发布在  iOS
关注(0)|答案(1)|浏览(220)

我正在用TypeScript开发一个homebridge插件,并使用axios处理网络请求(GitHub link).该插件登录本地网络中的服务器一次,然后定期轮询服务器以获取服务器的状态信息。如果会话过期,服务器将返回302错误并重定向到登录页面。因此插件不允许重定向,并在重试实际请求之前查找30 x错误,这意味着需要再次登录并更新会话。我使用一个助手函数来进行实际的网络请求,并且对该函数的每个调用都被 Package 在try { await...} catch { }块中。无论如何,偶尔会有一个错误跳过错误处理机制并传播回主事件循环,在那里它会导致插件崩溃,因为它没有得到处理。
相关代码如下:
在类构造函数中:

// [...]
    this.client = axios.create({
      baseURL: this.httpPrefix + this.IPAddress,
      timeout: 10000,
      maxRedirects: 0,
      method: "post",
      headers: {
        'content-type': 'application/x-www-form-urlencoded'
      }
    });
    // [...]

在轮询功能中:

[...]
    try {
      const response = await this.makeRequest('user/seq.xml', {'sess': this.sessionID});
      // [...]
    } catch (error) { throw(error); }

处理请求的实际helper函数:

private async makeRequest(address: string, payload = {}) {
    try {
      return await this.client({
        url: address,
        data: payload
      });
    } catch (error) {
      if (axios.isAxiosError(error)) {
        const response = error.response;
        if (response == undefined) throw error;
        if (response.status >= 300 && response.status < 400) {
          await this.login();
          payload['sess'] = this.sessionID;
          const response = await this.client({
            url: address,
            data: payload
          });
          return response;
        } else throw(error);
      } else throw (error);
    }
  }

下面这个函数负责按固定时间间隔调度轮询:

async updateAccessories() {
    // [...]
    try {
      await this.securitySystem.poll();
      // [...]
    } catch (error) {
      this.log.error((<Error>error).message);
      await delay(retryDelayDuration);
    }

    setTimeout(this.updateAccessories.bind(this), this.pollTimer);
  }

上面调用的delay函数是一个小助手,如下所示:

export function delay(milliseconds: number) {
    return new Promise(resolve => setTimeout(resolve, milliseconds));
}

本质上,homebridge服务器加载插件,在初始登录和附件发现之后,插件第一次调用updateAccessories函数,并且它自己将使用setTimeout来重新安排自己在pollTimer间隔之后再次运行。poll(),然后执行所有必要的逻辑来查询服务器、检索并解析所有相关数据以及更新数据模型,其思想是,如果一个轮询由于任何原因失败,插件应该优雅地掩盖它,并在下一次轮询尝试时重试。
您可以看到每个axios请求都是用await调用的,并被 Package 在try/catch块中以检查30个错误,helper函数本身也是用类似的机制调用的。理论上,所有的错误都应该被捕获并在程序逻辑的更高层处理。然而,我却遇到了这样的间歇性错误:

AxiosError: Request failed with status code 302
    at settle (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/axios/lib/core/settle.js:19:12)
    at IncomingMessage.handleStreamEnd (/usr/lib/node_modules/homebridge-caddx-interlogix/node_modules/axios/lib/adapters/http.js:495:11)
    at IncomingMessage.emit (node:events:525:35)
    at endReadableNT (node:internal/streams/readable:1358:12)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)

看起来好像一些axios失败的调用最终逃脱了错误处理,并进入了主事件循环,因此使程序崩溃。我做了一些搜索,并确保setTimeout代码是在try/catches之外调用的,但仍然经常出现错误。
有什么想法吗?先谢谢了。

pxyaymoc

pxyaymoc1#

更新:我没有设法实际阻止程序执行期间发生未捕获的302错误,但我用另一种方法解决了这个问题:我在类构造函数中为创建客户机对象添加了一个validateStatus选项:

[...],
  validateStatus: function (status) {
    return (status >= 200 && status < 400);
  }

这部分代码与maxRedirects结合使用:0使axios不遵循302重定向,但不认为它们是错误,这给了我机会在helper函数中手动处理这些状态代码,检查response.status值。只是把这个留给可能需要类似这样的东西的人:)

相关问题