fetch请求接受服务器返回流式数据

1,979 阅读3分钟

fetch请求是什么:

fetch 是一种用于发起网络请求的现代 JavaScript API,它旨在替代较为老旧的 XMLHttpRequestfetch 提供了一种更强大、灵活的方法来获取资源,并且基于Promise,使得代码更加简洁和易于阅读。

以下是 fetch 的一些关键特点:

  1. 基于 Promisefetch 返回一个 Promise,该 Promise 会在响应被成功返回时解析为 Response 对象。这意味着你可以使用 .then() 方法链式处理请求结果,也可以使用 async/await 语法来简化异步操作。
  2. 支持多种HTTP方法:虽然 fetch 默认是 GET 请求,但你也可以轻松地发送 POST 或其他类型的 HTTP 请求,只需要在选项对象中指定 method 参数。
  3. 更简单的接口:与 XMLHttpRequest 相比,fetch 拥有更简单的接口和更少的配置选项,这使得它更容易上手和使用。
  4. 更好的错误处理fetch 只在网络请求失败(例如,DNS 查询失败、连接失败等)时才会拒绝(reject)Promise。对于 HTTP 错误状态码(如 404 或 500),fetch 的 Promise 仍然会成功解决(resolve),因此你需要检查 Response.ok 属性来判断是否发生了 HTTP 错误。
  5. 流式数据读取fetch 还允许通过 response.bodyReadableStream 接口来流式读取大文件或服务器推送的数据。
  6. 不自动携带凭证:默认情况下,fetch 不会发送 cookies 等凭据信息,除非你显式设置了 credentials: 'include' 选项。
  7. 无跨域资源共享 (CORS) 支持fetch 遵循同源策略,并且可以处理跨域请求,只要服务器正确配置了 CORS 头。

这里是一个使用 fetch 发起 GET 请求的基本例子:

javascript
深色版本
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json(); // 如果响应内容是 JSON 格式,则解析为 JavaScript 对象
  })
  .then(data => console.log(data))
  .catch(error => console.error('There has been a problem with your fetch operation:', error));

以及一个发起 POST 请求的例子:

javascript
深色版本
fetch('https://api.example.com/post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    key1: 'value1',
    key2: 'value2'
  })
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

fetch API 在现代浏览器中得到了广泛的支持,但对于非常老的浏览器可能需要使用 polyfill 来提供兼容性。

使用fetch请求接收EventStream中服务器返回的流式数据示例

使用 fetch API 来接收服务器发送的事件流(Server-Sent Events, SSE),即 EventStream,可以通过创建一个持续开放的 HTTP 连接来实现,这个连接允许服务器向客户端推送更新。不过需要注意的是,fetch 本身并不是为了处理 SSE 而设计的,它更适合于一次性的请求-响应模式。但是,你可以通过设置 response.body 并读取 ReadableStream 来处理 SSE。

下面是一个简单的例子,展示了如何使用 fetchResponse.body 来监听来自服务器的事件流:

javascript
深色版本
if (!('fetch' in window)) {
  console.error('Fetch API is not supported in this browser.');
}

function createEventSource(url) {
  const eventSource = new AbortController();
  const signal = eventSource.signal;

  fetch(url, { signal })
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      // Read the response as a stream.
      const reader = response.body.getReader();
      const decoder = new TextDecoder('utf-8');

      function readStream() {
        reader.read().then(({ done, value }) => {
          if (done) {
            console.log('Stream complete');
            return;
          }

          // Decode and process each chunk of data.
          const decodedValue = decoder.decode(value, { stream: true });
          processEventStreamData(decodedValue);

          // Continue reading the stream.
          readStream();
        }).catch(err => {
          if (err.name !== 'AbortError') {
            console.error('Reading stream failed:', err);
          }
        });
      }

      readStream();

      // Return a function to abort the connection.
      return () => {
        eventSource.abort();
        console.log('Connection aborted');
      };
    })
    .catch(error => {
      console.error('Failed to fetch the event source:', error);
    });

  return eventSource;
}

// Function to handle incoming data from the stream.
function processEventStreamData(data) {
  // Implement your logic here for processing each message.
  console.log('Received data:', data);
}

// Start listening to the server-sent events.
const stopListening = createEventSource('/event-stream-endpoint');

// You can later call `stopListening()` to close the connection.

请注意,这段代码没有直接利用浏览器内置的 EventSource 对象,该对象是专门为 SSE 设计的,并且提供了更简洁的接口。如果你的目标是与 SSE 工作,那么建议你使用 EventSource。上述方法仅适用于需要更多控制或自定义的情况,或者在某些情况下 EventSource 不可用时。

要使用 EventSource,代码会更加简洁:

javascript
深色版本
const eventSource = new EventSource('/event-stream-endpoint');

eventSource.onmessage = function(event) {
  console.log('New message:', event.data);
};

eventSource.onerror = function(error) {
  console.error('EventSource failed:', error);
};

// When you want to disconnect:
// eventSource.close();