加载中...

使用post请求建立长连接实现SSE

一、背景

SSE 是指服务器发送事件(Server-Sent Events),它是一种用于在客户端和服务器之间单向实时通信的 Web 技术。通过 SSE,服务器可以向客户端发送异步事件流,而无需客户端发起请求。

参考:一文读懂即时更新方案:SSE - 掘金

原生的EventSource 不能使用post方法,只能使用get方法,而且还不能自定义请求header,所以这里使用npm包

二、npm包

event-source-polyfill:可以自定义请求头

fetch-event-source:可使用post请求,但没正式发布,使用的人不多

de:自定义请求头,且可使用post请求,不兼容IE浏览器

三、实现代码

  1. import { EventSourceMessage, fetchEventSource } from "@microsoft/fetch-event-source"
  2. export function usePrompt() {
  3. const store = useLocalStore(()=>{
  4. submitCustomPrompt: async ({ id, content, qasStore, isReload }) => {
  5. try {
  6. let text = "";
  7. const url = `${protocol}${hostname}${port}/api/custom-prompt`
  8. await fetchEventSource(url,
  9. {
  10. method: "POST",
  11. headers: {
  12. Authorization: `Bearer ${auth.oauth2Token?.access_token}`,
  13. "Content-Type": "application/json",
  14. },
  15. body: JSON.stringify({
  16. id: id,
  17. content: content,
  18. }),
  19. onmessage(event: EventSourceMessage) {
  20. let data: customPromptPostRes = { results: "", results_id: "", status: "processing" };
  21. try {
  22. data = JSON.parse(event.data);
  23. } catch {
  24. console.error("onmessage error");
  25. }
  26. if (!data.results_id) return;
  27. qasStore.updatePromptCard({
  28. text: data.results,
  29. idx: qasStore.contents.length - 1,
  30. err: !data.results,
  31. });
  32. if (data?.status === "completed") {
  33. text = data.results || intl.formatMessage({ id: "cognitive.prompt.empty" });
  34. }
  35. },
  36. onerror(error) {
  37. throw error;
  38. },
  39. async onopen(response) {
  40. if (response?.status !== 200) {
  41. throw response;
  42. }
  43. }
  44. }
  45. );
  46. return text;
  47. } catch (err) {
  48. if (err?.status === 401) {
  49. await getRefreshToken();
  50. store.submitCustomPrompt({
  51. id,
  52. content,
  53. qasStore,
  54. isReload: true,
  55. });
  56. return "";
  57. };
  58. if(err?.status) {
  59. message.warning(intl.formatMessage({ id: `err.${err?.status}` }));
  60. } else {
  61. message.warning(toastErrPointHandle(intl.formatMessage({ id: "err.500000000" })));
  62. }
  63. return "";
  64. }
  65. },
  66. })
  67. return store;
  68. }
  69. export default usePrompt;

ps:后端的报错(连接成功后的操作)需要通过onopen(response){}才能拿到;而连接错误的信息需要通过onerror(error){}拿到