前言
在前端开发中,跨域是一个不可避免的问题。跨域指的是不同源的网页之间进行数据传输时涉及到的安全限制。Server-sent Events(SSE)作为一种轻量级的服务器推送技术,在前端开发中得到了广泛应用。然而,SSE 也不能避免跨域问题。本文旨在介绍如何解决 Server-sent Events 跨域使用方法及常见问题,帮助前端开发者更好地使用 SSE。
什么是Server-sent Events
Server-sent Events(SSE)是一种客户端向服务器建立单向通信连接的 API ,用来接收服务器的推送消息。与 Websocket 相比,SSE 具有轻便、简单易用等优点。
SSE 支持在服务器向客户端推送事件流,这个事件流被称之为稍后会讲到的 SSE Event Stream。SSE 允许后端服务端不断推出数据,而前端通过 SSE API 监听,一旦有新数据,就能即时得到。SSE 也支持自动重连机制,若连接中断,可以自动重连,从而避免数据丢失。
SSE的响应格式如下:
-- -------------------- ---- ------- ------------- ----------------- ----- ---- -- --- ----- ------- ----- ---- -- --- ------ ------- --- - ----- ---- -- --- ----- ------- --- --- ------ -------- ----- ---- -- --- ----- -------
其中,“Content-Type: text/event-stream” 代表响应的类型是 SSE Event Stream,接下来的数行代表每一个 SSE 事件。
- 以 "data:" 打头的每一行都是一个 SSE 数据项。
- 以 "id:" 开头的行表示该消息的事件ID。
- 以 "event:" 开头的行表示 SSE 事件名称,HTML5 规范定义了通过这种方式使用 SSE 自定义事件,这些自定义事件都是从默认的 message 事件派生而来的。
SSE的跨域问题
当 SSE 的域名与访问的页面域名不同时,就会产生跨域问题。与其他网络请求不同的是,SSE 的 cross-origin 是必须的,否则 SSE 请求会被同源策略拒绝。
常见的处理 SSE 跨域问题的方案有两种,一种是利用代理(proxy)技术,在服务器与前端之间建立代理层,将请求和响应进行中转;另一种则是使用 CORS 技术,也就是跨源资源共享。
代理方式解决SSE跨域问题
代理(proxy)技术,就是在前端与服务器之间增加一个代理层,将请求和响应进行中转,使请求能够顺利发出并获取到响应。
下面是一个简单的代理发送 SSE 服务的实例:
-- -------------------- ---- ------- ----- ------ - --------------------------- ----- ----------- - --- -------------------------------------------------------- --------------------- - ------------- -- - ------------------------------ -- ------------------- - ------- -- - ------------------- -------------------- --
这里通过监听 eventSource
的 onmessage
事件,当服务端推送消息时,调用回调函数。
下面是代理服务端代码实现:
-- -------------------- ---- ------- ----- ---- - ---------------- ----- ------- - ------------------- ----------------------- ---- -- - ----------------------------- --------------------- ------------------------------ ------------ --------------------------- -------------- -------------------------------------------- ----- ----- ------ - --------- ---- -------------- -------- - -- -------- --------- -------- ------------- ------ -- ------ ----------------------------------------- -- -------- -- - ----------------- --- --------------- -- -- - --------------- --- ---------------- -------- --------------- - ------ --------- - --------------------------------- ---- -
这里的代理服务器代码使用 Node.js 实现。
这样,当我们在前端访问 /proxy?url=http://my-server.com/sse
的时候就会代理到后端服务站点。因为我们在代理中设置了 Access-Control-Allow-Origin: *
,所以在浏览器端请求时,同样需要带参数 withCredentials: true
去获取 Cookie 等信息。
跨源资源共享(CORS)解决SSE跨域问题
CORS 是一个 W3C 标准,全称是 Cross-Origin Resource Sharing(跨源资源共享)。 它是一个机制,用来让 Web 应用从不同的源访问服务器资源,当跨域请求时使用。它规定了一些必须在服务端设置的 HTTP 头,使得浏览器可以安全地访问跨域的资源。
下面是设置CORS的服务器代码(示例代码中用了Express框架):
-- -------------------- ---- ------- ----- ------- - ------------------- ----- --- - ---------- ------------- ---- ----- -- - -------------------------------------------- ----- --------------------------------------------- ----------------------- -------------- ------------------------------- -------------- ----------------------------------------------- ---------------------------- --------------- ----------------- -- ------------------------------------------------- -------- ------- --- --------------------- ----- ---- -- - -------------------- - --------------- ---------------------------------- ---------------- ----------- ----------- ------------ --- ---------------- ------ ----- ----- ------- ------- --- -------- - -------------- -- - ---------------- ------ ----- ----- ------- ------- -- ------ --------------- -- -- - ------------------------ --- --- ---------------- -- -- - ------------------- -- ------- -- ---- ------- ---
这里的重点在于设置了响应头中的 Access-Control-Allow-Origin
属性。这里设置的是允许任何来源进行请求。
下面是前端代码,我们在前端通过 SSE 获取服务端推送过来的时间信息:
-- -------------------- ---- ------- ----- ------ - --- ----------------------------------------------- ------------------------ ---------- --------------- - ------------------------ -- ----- -- ------------------------ ------- --------------- - ---------------------- -- ------ ------ -- ----- -- ------------------------ -------- --------------- - ------------------ --------- -- ------- --------------- -- ----- --
这里的事件监听是在 source
上注册了三个事件监听器,分别对应了消息推送、连接开启以及连接错误。当服务端主动向客户端推送时,会触发 message
事件。在这里,我们用 console.log(event.data)
将推送内容输出到控制台。
常见问题解决方案
SSE的连接断开问题
由于 SSE 是基于 HTTP/1.1 协议实现的,因此当服务端没有数据更新时,连接会一直保持,此时如果没有处理,就会造成连接状态丢失。此时,我们可以使用 EventSource.onerror
事件来处理这个问题。
-- -------------------- ---- ------- -- ------- ----------- --- ------------ - ----- ----------- - --- ----------------- ------------------ - ---------- - ---------------- ---------- -------- -- --------------------- - --------------- - ----------------- --------- - - ------------ -- ------------------- - --------------- - -- ----------------------- --- ----------------------- - ------------------------- --- ---------------- - ---- - ---------------- ---------- -------- - -- - ---- - ------------------ --- --------- -- ---- ---------- -
在这里,我们为 EventSource
实例添加了 onerror
回调,当连接开启时,会调用 onopen
回掉函数,在控制台输出连接开启的信息。而当服务端没有数据更新时,连接会被关闭,此时我们在 onerror
回掉中进行了控制台输出,打印Reconnecting SSE Connection...
和 SSE Connection lost!
等信息。当然也可以在这里进行自动重连。
服务器关闭时,如何正确关闭SSE的连接
服务端上使用 SSE 推送消息时,一旦关闭服务,客户端对应的 SSE 通道也会关闭。这时,我们需要在客户端监听 SSE.onerror
事件,通知服务端结束 SSE 事件流。
-- -------------------- ---- ------- -- ------------------ -------- -------------------- - -- ----- - -- ---------------- ----- --- - --- ----------------- ---------------- -------------------- ----------- - ------------ - -- ------- ----------- --- ------------ - --- - --- ---------------- - ---------------- ---- --- ------------- - --------------- - -------------------- ----------- -- ----------- - --------------- - ------------------ ----------- ------------ -- -
在这里,我们通过向服务端发送一个 POST 请求以正常结束 SSE 事件流。
结语
上文我们讲解了如何解决 Server-sent Events 跨域使用方法及常见问题,SSE 能够帮助前端开发者更加轻松地实现实时数据更新。虽然 SSE 具有一定的局限性,但是在一些简单的实时推送场景下,它是一个非常有效地实现方案,值得广泛应用。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67c18f3f314edc26849ec0a9