How can I use Axios with Server-sent events (SSE) in the browser?

题意:如何在浏览器中使用 Axios 处理服务器发送事件(SSE)?

问题背景:

I'm trying to receive Server-sent events (SSE) in the browser. My application uses axios (v1.6.8) to make API calls. This code works in node:

我正在尝试在浏览器中接收服务器发送事件(SSE)。我的应用程序使用 axios(v1.6.8)进行 API 调用。以下代码可以在 Node.js 中运行:

const getStreamAxios = () => {
  axios.get('https://my-domain.com/api/getStream', {
    responseType: 'stream',
    headers: {
      'Accept': 'text/event-stream',
    }
  })
    .then(response => {
      console.log('axios got a response');
      const stream = response.data;

      stream.on('data', data => {
          console.log(data.toString('utf8'));
      });
      
    })
    .catch(e => {
      console.error('got an error', e);
    });    
}

But in the browser, the Promise is never fulfilled (i.e.: I don't get the console message "axios got a response"). Instead I get this warning in the console:

但是在浏览器中,Promise 永远不会被解决(即:我没有看到控制台信息 "axios got a response")。相反,我在控制台中收到了这个警告:

The provided value 'stream' is not a valid enum value of type XMLHttpRequestResponseType.

The issue is not in the API endpoint; in addition to checking it with node, I have verified it also works with Apidog and EventSource in the browser.

问题不在于 API 端点;除了使用 Node.js 进行检查外,我还验证了它在 Apidog 和浏览器中的 `EventSource` 上也能正常工作。

问题解决:

The root of the problem is, axios as of v1.6.8, uses XMLHttpRequest in the browser (see axios docs). This means you can only use responseType: 'stream' in node.

问题的根源在于,axios 从 v1.6.8 开始在浏览器中使用的是 `XMLHttpRequest`(参见 axios 文档)。这意味着你只能在 Node.js 中使用 `responseType: 'stream'`。

This was finally addressed in v1.7.0, which allows you to use fetch instead of XHR, like so:

这个问题最终在 v1.7.0 中得到了处理,该版本允许你使用 `fetch` 而不是 `XHR`,如下所示:

const getStreamAxios = () => {
  axios.get('https://my-domain.com/api/getStream', {
    headers: {
      'Accept': 'text/event-stream',
    },
    responseType: 'stream',
    adapter: 'fetch', // <- this option can also be set in axios.create()
  })
    .then(async (response) => {
      console.log('axios got a response');
      const stream = response.data;      

      // consume response
      const reader = stream.pipeThrough(new TextDecoderStream()).getReader();
      while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        console.log(value);
      }
    })
    // catch/etc.
}

If you are really invested in an older version of axios, there is a dodgy-looking workaround, using the onDownloadProgress option:

如果你非常依赖旧版本的 axios,可以使用一个看起来不太可靠的解决方法,即使用 `onDownloadProgress` 选项:

const getStreamAxios = () => {
  axios.get('https://my-domain.com/api/getStream', {
    headers: {
      'Accept': 'text/event-stream',
    },
    onDownloadProgress: (evt) => {
      // Parse response from evt.event.target.responseText || evt.event.target.response
      // The target holds the accumulator + the current response, so basically everything from the beginning on each response
      // Note that it's evt.target instead of evt.event.target for older axios versions
      let data = evt.event.target.responseText;
      console.log(data);
    }
  })

Caveat

Note that, regardless of the option you choose, you will still have to write something to consume the server-sent events. Here's an example using @server-sent-stream/web:

请注意,无论你选择哪种方案,你仍然需要编写代码来处理服务器发送的事件。以下是一个使用 `@server-sent-stream/web` 的示例:

const getStreamAxios = () => {
  // axios.get(...) etc.
  .then(async (response) => {
    console.log('axios got a response');
    const stream = response.data; // <- should be a ReadableStream

    const decoder = new EventSourceStream();
    stream.pipeThrough(decoder);

    // Read from the EventSourceStream
    const reader = decoder.readable.getReader();

    while (true) {
      const { value, done } = await reader.read();
      if (done) break;

      // The value will be a `MessageEvent`.
      // MessageEvent {data: 'message data', lastEventId: '', …}
      console.log(value)
    }
  })
}

If you don't want to parse this yourself, you can look at the native EventSource API. If you need more options not provided by EventSource (such as setting additional headers), you can take a look at options like sse.js, or this extended EventSource implementation

如果你不想自己解析这些事件,可以查看原生的 `EventSource` API。如果你需要 `EventSource` 未提供的更多选项(如设置额外的请求头),可以考虑 `sse.js` 或这个扩展版的 `EventSource` 实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

营赢盈英

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值