解决Server-sent Events跨域使用方法及常见问题

阅读时长 11 分钟读完

前言

在前端开发中,跨域是一个不可避免的问题。跨域指的是不同源的网页之间进行数据传输时涉及到的安全限制。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 服务的实例:

-- -------------------- ---- -------
----- ------ - ---------------------------
----- ----------- - --- --------------------------------------------------------

--------------------- - ------------- -- -
    ------------------------------
--

------------------- - ------- -- -
    -------------------
    --------------------
--

这里通过监听 eventSourceonmessage 事件,当服务端推送消息时,调用回调函数。

下面是代理服务端代码实现:

-- -------------------- ---- -------
----- ---- - ----------------
----- ------- - -------------------

----------------------- ---- -- -
    ----------------------------- ---------------------
    ------------------------------ ------------
    --------------------------- --------------
    -------------------------------------------- -----

    ----- ------ - ---------
        ---- --------------
        -------- -
            -- --------
            --------- --------
            ------------- ------
        --
        ------ -----------------------------------------
    -- -------- -- -
        -----------------
    ---

    --------------- -- -- -
        ---------------
    ---

----------------

-------- --------------- -
    ------ --------- - --------------------------------- ----
-

这里的代理服务器代码使用 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

纠错
反馈