如何在 NextJS API 中转发服务器发送事件

7

我有一个类似于这样的Next.js api(/api/events/index.js):

import EventSource from 'eventsource';
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';

export default withApiAuthRequired(async function handler(req, res) {
  if (req.method === 'GET') {
    const { accessToken } = await getAccessToken(req, res);

    res.writeHead(200, {
      Connection: 'keep-alive',
      'Cache-Control': 'no-cache',
      'Content-Type': 'text/event-stream',
    });
    const eventSource = new EventSource(
      `http://localhost:8000/v1/event-stream`,
      { headers: { Authorization: `Bearer ${accessToken}` } },
    );

    eventSource.onmessage = (e) => {
      res.write(`data: ${e.data}\n\n`);
      //  res.end();
    };
  }
});


基本上,我正在尝试在后端API SSE端点上发出请求,该请求需要一个授权承载令牌(最好,访问令牌应仅在Next.js API端可用)。收到事件后,它应在新的事件流响应中写入。
在React客户端代码中,我只需按如下订阅这些SSE:
const IndexPage = () => {
  
  useEffect(() => {
    const eventSource = new EventSource(`http://localhost:3000/api/events`);
    eventSource.onmessage = (e) => {
      console.log(e);
    };
    return () => {
      eventSource.close();
    };
  }, []);

  return <div></div>;
}

问题出在哪里?客户端没有收到任何信息。当我尝试在 API 端调用 res.end() 时,客户端会接收到一个事件,但事件流会不断重新连接,大约每秒钟一次。基本上,连接没有保持活动状态。我如何转发我的 SSE 结果?

1
嗨!我在本地复制了您的问题(没有使用nextjs-auth0),并发现当我没有以下Cache-Control头时,它会出错:“no-cache,no-transform”。这是假设设置中的其他所有内容都按预期工作。我参考了这个github问题:https://github.com/vercel/next.js/issues/9965 - rexessilfie
@rexessilfie 嘿嘿!确实有帮助。现在运行良好,除了一个小但重要的问题... API 端的事件流在客户端断开连接时没有关闭。基本上,在页面刷新时,会建立一个新的连接到 http://localhost:8000/v1/event-stream。这绝对是可怕的... - Branislav Lazic
你考虑过这种方法吗?在下一个API SSE端点的第一次请求中,添加一个唯一标识客户端的cookie,并将响应对象保存在以此唯一标识为键的映射中。当您的下一个API从后端源接收到SSE时,将其传播到映射中的所有客户端。在客户端上,您可以在单独的(或相同的端点)上设置轮询,以告诉您的下一个API保持连接打开。如果您的API在X时间内没有收到客户端的ping,则可以结束连接。这听起来怎么样? - rexessilfie
嘿 @BranislavLazic,如果您正在使用第一个答案和 no-cache. no-transform 以及 /api/events/index.js 中的这一部分: req.on("close", async () => { reader.cancel(); res.end(); }); - slumbering
@rexess 一年后的回应... :) 虽然你的建议听起来不错,但它有点否定了SSE的整个目的。如果我必须轮询Next服务器以检查是否有开放的连接,那么轮询本身可能就是解决整个问题的方法。基本上,从客户端轮询Next服务器,并使用访问令牌将请求传递给API。是的,SSE会更加实时,但还是... - undefined
显示剩余2条评论
1个回答

1
问题出现的原因是默认的Next.js服务器默认压缩所有内容。
只需在您的writeHead阶段添加'Content-Encoding': 'none',一切都应该按照您的意愿工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接