如何在不用EventSource的情况下,用POST请求SSE (Server-Sent Events)

1,458 阅读2分钟

实现效果

gt_2024422_30826_v_gif.gif

处理步骤

关于SSE的请求,搜集到的大多数文章都会告诉我们要用GET的方法才可以接收流式数据,但当我们拿到一个需要用POST方法去进行SSE请求的接口时, 此时原生EventSource并不支持。这个时候,我们可以尝试用如下步骤进行处理:

  1. 首先,我们将使用fetch向 SSE 端发出POST请求,响应标头要设置成ContentType: text/event-stream
  const response = await fetch('/sse', {
    method: 'POST',
    headers: {
      'Content-Type': 'text/event-stream'
    },
    body: {
      "user_id": 123
    }
  })
  1. 创建一个reader实例来获取从服务器收到的网络请求。
// 方式1:以字符串形式接收数据,我们通过 pipelinethrough 方法将其转化成TextDecoderStream(文本解码器流,这个流可以将字节流,比如网络请求的响应,转换为Unicode字符串),再用 getReader() 方法返回一个 reader 对象。
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()

// 方式2:以字节数组形式接收数据,我们直接调用 getReader()
const reader = response.body.getReader();
  1. 创建一个循环以继续接收消息,直到触发信号完成结束接收。
while (true) {
  const {value, done} = await reader.read();
  if (done) break;
  console.log('Received', value);
}

在Angular应用中的代码示例

// chat.servive.ts 文件

// 中间件,用于回传信息
sseDataSubject: Subject<string> = new Subject<string>();

async postStream(param) {

    const response = await fetch(yourUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'text/event-stream'
        },
        body: JSON.stringify(param)
      })
    const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()
    while (true) {
      const {value, done} = await reader.read();
      if (done) {
        this.sseDataSubject.next('done'); // 传一个done的标识
        break;
      }
      this.sseDataSubject.next(value); // 发送消息
      console.log('Received', value);
    }
}
// chat.component.ts 文件

constructor() {

    // stream数据监听
    this.chatService.sseDataSubject.subscribe(sseData => {
        
        if (sseData === 'done') {
            // loading状态消失、清除输入框内容、记录上一轮ai对话返回的信息
            ...
            return;
        }
        const lastRole = this.conversionList[this.conversionList.length - 1].role;
        
        // 未返回'done',继续拼接句子
        ...
        
        // 用 ElementRef 制作滑动到底部的动画
        this.scrollToBottom();
})

}

1. 为什么不用 @microsoft/fetch-event-source ?

本来按照这篇文章《Implement SSE with Spring Boot And Angular》,但结合npm的文档,其中onmessage方法并没有按照预期打印出返回信息。

2. 为什么没有用Angular的HttpClient尝试POST请求?

issue贴中有回复”text/event-stream是服务器发送事件(SSE)的媒体类型,我们不能与 HttpClientModule 所基于的 XHR 请求混合。“

参考与拓展资料