首页 > 其他分享 >如何配合后端SSE实现文字打字机效果:

如何配合后端SSE实现文字打字机效果:

时间:2024-08-20 10:55:27浏览次数:13  
标签:后端 text response 打字机 SSE message data event

后端的同学已经做好了后端的SSE,并给出了我们选型的原因:报告。我就不再赘述,主要还是从前端的实现来阐述。

EventSource:


        SSE(Server-Sent Events,服务器发送事件)是一种实现服务器到客户端单向实时通信的技术。它允许服务器在有新数据可用的任何时候向浏览器推送信息,而不需要浏览器不断地询问服务器是否有新数据。这是通过在客户端和服务器之间建立一个持久的连接来实现的,服务器通过这个连接推送数据。

        EventSource 是一个JavaScript接口,它为服务器发送事件提供了一种简单的实现方式。使用 EventSource,开发者可以非常容易地创建一个连接到服务器的客户端,并监听服务器推送的事件。当服务器发送一个事件时,EventSource 对象会触发一个事件,开发者可以监听这个事件并相应地处理。

        在SSE中,浏览发送一个请求给服务端,通过响应头中的Content-Type:text/event-stream等向客户端声名这是一个长连接,发送的是流数据,这样客户端就不会关闭连接,一直等待服务端发送数据。

注意,sse本身是不支持post的方式,可以通过fetch的方式完成post相关操作。

post方式
使用post方式实现的大概格式如下:

// 创建一个AbortController实例,用于在未来某个时刻取消fetch请求
const controller = new AbortController();

// 获取由AbortController实例控制的信号对象
const signal = controller.signal;

// 使用fetchEventSource发起一个到指定URL的POST请求,用于建立SSE连接
fetchEventSource(`${url}`, {
  method: 'POST',           // 请求方法为POST
  signal: signal,           // 将信号对象传递给请求,以便能够取消请求
  headers: {                // 设置请求头
    'Content-Type': 'application/json',  // 请求内容类型为JSON
  },
  body: JSON.stringify(message),  // 请求体,将message对象转换为JSON字符串
  onmessage(msg) {           // 定义收到消息时的回调函数
    // 当服务器发送消息时,这里会接收到消息并处理
  },
  one rror(err) {             // 定义出错时的回调函数
    throw err;              // 如果发生错误,抛出这个错误
    // 抛出错误将中断fetchEventSource的处理流程
  }
});

onmessage回调函数不触发
具体实现时,我发现log的onmessage消息全部为空。但是使用apipost测试后端接口可以得到流式输出。

进一步查看network,我发现其中的response中有响应,格式如下:(因为返回的是字符串且默认解码方式为utf-8所以没有成功解码)

{"docs": ["<span style='color:red'>\u672a\u627e\u5230\u76f8\u5173\u6587\u6863,\u8be5\u56de\u7b54\u4e3a\u5927\u6a21\u578b\u81ea\u8eab\u80fd\u529b\u89e3\u7b54\uff01</span>"]}

{"text": "\u60a8\u597d", "message_id": "148994c7260445e9a23736808658b4fd"}

但是EventStream中全部为空。

使用onopen方法,控制台输出一切正常:

async onopen(response) {
            console.log(response);
            if (response.ok && response.headers.get('content-type') === 'text/event-stream') {
              console.log(response);
                return; 
            } else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
                // client-side errors are usually non-retriable:
                console.log("回应错误")
            } else {
                // throw new RetriableError();
            }
        },

说明只有onmessage的实现有问题。

查阅资料,我发现原因是因为SSE规定了schema,如果不符合下面的格式,前端无法解析数据

SSE规定的schema

event: message\n
data: {数据}\n\n

以上为纯字符串,同时message也可以是其他消息,不会影响onmessage回调函数。

可以发现后端的返回格式并不如我们所愿,更改后端返回的代码如下:

# event_data = json.dumps(data)
event_data = json.dumps({"data":data})

# yield f"{event_data}\n\n"
yield f"event: message\ndata: {event_data}\n\n"

重启后端,重新运行,将onmessage中的msg log出来,结果如下:

再查看EventStream,已经成功解析出来了:

聊天打字机输出

可以看到后端返回的消息中,其中的 Unicode字符未被正确转义,首先需要使用JSON.parse() 方法来解码它。

const parsedData = JSON.parse(msg.data);

查看data的格式,分为两种,一种里面为完整的docs文本,另一种是需要拼接的text文本。

event: message
data: {"data": {"docs": ["<span style='color:red'>\u672a\u627e\u5230\u76f8\u5173\u6587\u6863,\u8be5\u56de\u7b54\u4e3a\u5927\u6a21\u578b\u81ea\u8eab\u80fd\u529b\u89e3\u7b54\uff01</span>"]}}

event: message
data: {"data": {"text": "\u5468", "message_id": "d67aac633ac0456f9945d3d0c5f20f45"}}

对于docs,我直接将其添加到aichat中,但是注意要将其中的换行符切换为<br>

let aiCurrentChatDocs = JSON.stringify(parsedData.data.docs);
aiCurrentChatDocs = aiCurrentChatDocs.replace(/\n/g, '<br>');
// 将ai回复加入list
AIReplay('参考:'+ aiCurrentChatDocs);

对于text,我使用resultAnswer拼接目前为止得到的text的字符串。

然后找到msgList中最后一条ai的消息(也就是消息内容和resultAnswer相等的消息),如果没有的话,说明当前是接收到的第一个token,于是创建AIReplay;如果可以找到的话,将当前的lastAI的content直接更改为resultAnswer.value。

因为msg是reactive创建的,所以可以动态修改。

// 将ai回复加入list
let lastAI = msgList.find((msg) => msg.content === resultAnswer.value);
resultAnswer.value +=parsedData.data.text;
//如果没有回答就创建新的:
if(!lastAI){
	AIReplay(resultAnswer.value);
}
else{
	lastAI.content=resultAnswer.value;
}

标签:后端,text,response,打字机,SSE,message,data,event
From: https://blog.csdn.net/qq_38040571/article/details/141352461

相关文章

  • 第一次项目搭建笔记&路由导航守卫&web前后端会话跟踪
    1.重新搭建后端项目在IDEA中重新创建一个JavaEE项目,记得勾选Webprofile之后在java文件中重新搭建分级的文件夹按照标准创建com.xxxx.dorm文件夹并创建dao(数据处理),filter(过滤器),model(模型),util(工具),web(服务端)等文件夹进行不同功能部分的分类搭建完基本的框......
  • 动态表单后端设计
     动态表单通常用于收集各种不同类型的数据,这些数据可能随时间变化或根据用户的需求而变化。因此,数据库设计和接口设计需要足够灵活以适应不同的表单结构。以下是一些关于动态表单的数据库设计和接口设计的基本指导原则:数据库设计表单元数据表:form_id(表单ID)form_name(表......
  • WindowsServer系统下nginx代理问题
    部署vue打包后的dist文件夹后,重启nginx发现没生效,操作如下:1.停止redis,删除浏览器缓存并用无痕模式访问发现依然不生效,试着各种办法重新导入数据库数据也不行,nginx.exe-sstop依然能访问2.搜索发现可能是WindowsServer系统的问题,于是执行命令nginx-squit退出nginx......
  • ceph-messenger模块代码走读(1)
    messenger代码走读messenger的使用以mgr代码为例,看看messengrr的初始化和启动。//构造函数,初始化一个client_messenger对象。MgrStandby::MgrStandby(intargc,constchar**argv):Dispatcher(g_ceph_context),monc{g_ceph_context,poolctx},client_messenger(M......
  • Django前后端交互问题
    利用Django搭建网站通过最近的学习,我发现利用Django搭建网站时,遇到的让我印象最深的难题就是—前后端交互问题,那么既然说到了前后端交互,肯定是有前端,后端两个大模块前端Django搭建的项目中,前端通常是在templates下建立一个html文件,在这个文件中写界面,也有挺多小细节,我就......
  • 计算机毕业设计 小区运动中心预约管理系统 Java+SpringBoot+Vue 前后端分离 文档报告
    ......
  • 基于微信小程序的外卖点餐系统的设计与实现22 毕业论文+开题报告+答辩PPT+论文检测查
    !!!有需要的小伙伴可以通过文章末尾名片咨询我哦!!! ......
  • springboot+vue前后端分离项目-项目搭建19-ElementUI图标+聊天室
    一、ElementUI图标按照官网这两步,注册所有图标,然后就能直接使用 1.安装后在vue/package.json里能看到包 2.注册所有图标 3.点击自动复制,直接就能使用 4.效果: ......
  • 颗粒流 + Janssen 定律 + Bagnold 数
    对于\(n\)个球,易得有\[\begin{array}{c}\displaystyle\frac\pi2>\theta_i>-\frac\pi2,\theta_1>\cdots>\theta_i>\cdots>\theta_{n-1}\\[1ex]\displaystyle\foralli\nej,\left\lvert\sum_{k=1}^ir_k-\sum_{k=1}^jr_k\right\rve......
  • Java后端实现ppt格式转为pdf格式文件
    (1)使用场景:将从web前端上传到后端的ppt格式的文件转换为pdf格式的文件。项目框架为springboot+layui(2)实现方法:1、步骤1:导入所需jar包,如下<!--ppt转pdf--><dependency><groupId>com.aspose</groupId><artifactId>aspose-word</artifactId><version>18.10&l......