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

1,325 阅读1分钟

一、背景

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

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

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

二、npm包

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

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

@microsoft/fetch-event-source:自定义请求头,且可使用post请求,不兼容IE浏览器

三、实现代码

import { EventSourceMessage, fetchEventSource } from "@microsoft/fetch-event-source"
export function usePrompt() {
    const store = useLocalStore(()=>{
        submitCustomPrompt: async ({ id, content, qasStore, isReload }) => {
            try {
                let text = "";
                const url = `${protocol}${hostname}${port}/api/custom-prompt`
                await fetchEventSource(url,
                    {
                        method: "POST",
                        headers: {
                            Authorization: `Bearer ${auth.oauth2Token?.access_token}`,
                            "Content-Type": "application/json",
                        },
                        body: JSON.stringify({
                            id: id,
                            content: content,
                        }),
                        onmessage(event: EventSourceMessage) {
                            let data: customPromptPostRes = { results: "", results_id: "", status: "processing" };
                            try {
                                data = JSON.parse(event.data);
                            } catch {
                                console.error("onmessage error");
                            }
                            if (!data.results_id) return;
                            qasStore.updatePromptCard({
                                text: data.results,
                                idx: qasStore.contents.length - 1,
                                err: !data.results,
                            });
            
                            if (data?.status === "completed") {
                                text = data.results || intl.formatMessage({ id: "cognitive.prompt.empty" });
                            }
                        },
                        onerror(error) {
                            throw error;
                        },
                        async onopen(response) {
                            if (response?.status !== 200) {
                                throw response;
                            }
                        }
                    }
                );
                return text;
            } catch (err) {
                if (err?.status === 401) {
                    await getRefreshToken();
                    store.submitCustomPrompt({
                        id,
                        content,
                        qasStore,
                        isReload: true,
                    });
                    return "";
                };
                if(err?.status) {
                    message.warning(intl.formatMessage({ id: `err.${err?.status}` }));
                } else {
                    message.warning(toastErrPointHandle(intl.formatMessage({ id: "err.500000000" })));
                }
                return "";
            }
        },
    })
    return store;
}
export default usePrompt;

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