如何在express.js中使用server-sent-events

问题描述 投票:3回答:5

我用express.js设置我的REST服务器。现在我想将sse添加到此服务器。在我实现了this sse包之后,我收到了一个错误。我知道我得到这个错误,什么时候尝试使用res.send两次,但我不是。

ERROR: Error: Can't set headers after they are sent.                                            
    at ServerResponse.OutgoingMessage.setHeader (http.js:690:11)                            
    at ServerResponse.header (/home/root/node_modules/express/lib/response.js:718:10)         
    at ServerResponse.send (/home/root/node_modules/express/lib/response.js:163:12)            
    at app.get.str (/home/root/.node_app_slot/main.js:1330:25)                                 
    at Layer.handle [as handle_request] (/home/root/node_modules/express/lib/router/layer.js:95:5)    
    at next (/home/root/node_modules/express/lib/router/route.js:131:13)                       
    at sse (/home/root/node_modules/server-sent-events/index.js:35:2)                          
    at Layer.handle [as handle_request] (/home/root/node_modules/express/lib/router/layer.js:95:5)  
    at next (/home/root/node_modules/express/lib/router/route.js:131:13)                          
    at Route.dispatch (/home/root/node_modules/express/lib/router/route.js:112:3)

是否有可能我不能在sse函数中使用express方法?例如:

app.get('/events', sse, function(req, res) {
    res.send('...');
});

此外,我发现this解决方案和this。是否有可能使用res.write功能或以其他方式使用其他方式?

javascript node.js express server-sent-events
5个回答
3
投票

没有其他包,你绝对可以做到这一点。

我写了一篇关于此的博客文章,part 1列出了基础知识。

您不得关闭SSE,因为这会破坏功能。重点是它是一个开放的HTTP连接。这允许在任何时候将新事件推送到客户端。


1
投票

从你正在使用的库的文档中可以看出,当你在函数中使用它作为中间件时,你应该使用res.sse。见:https://www.npmjs.com/package/server-sent-events

但是,正如你所提到的,所有这一切实际上是从他们的代码中包装res.write。见:https://github.com/zacbarton/node-server-sent-events/blob/master/index.js#L11


1
投票

自我推销:我编写了ExpreSSE包,提供了在快递中使用SSE的中间件,你可以在npm:@toverux/expresse找到它。

一个简单的例子:

router.get('/events', sse(/* options */), (req, res) => {
    let messageId = parseInt(req.header('Last-Event-ID'), 10) || 0;

    someModule.on('someEvent', (event) => {
        //=> Data messages (no event name, but defaults to 'message' in the browser).
        res.sse.data(event);
        //=> Named event + data (data is mandatory)
        res.sse.event('someEvent', event);
        //=> Comment, not interpreted by EventSource on the browser - useful for debugging/self-documenting purposes.
        res.sse.comment('debug: someModule emitted someEvent!');
        //=> In data() and event() you can also pass an ID - useful for replay with Last-Event-ID header.
        res.sse.data(event, (messageId++).toString());
    });
});

还有另一个中间件可以将相同的事件推送到多个客户端。


1
投票

新答案:

只需使用socket.io,它就更容易也更好! https://www.npmjs.com/package/socket.io#in-conjunction-with-express

基本设置:

const express = require('express');
const PORT = process.env.PORT || 5000;
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
// listen to socket connections
io.on('connection', function(socket){
  // get that socket and listen to events
  socket.on('chat message', function(msg){
    // emit data from the server
    io.emit('chat message', msg);
  });
});
// Tip: add the `io` reference to the request object through a middleware like so:
app.use(function(request, response, next){
  request.io = io;
  next();
});
server.listen(PORT);
console.log(`Listening on port ${PORT}...`);

在任何路由处理程序中,您可以使用socket.io:

app.post('/post/:post_id/like/:user_id', function likePost(request, response) {
  //...
  request.io.emit('action', 'user liked your post');
})

客户端:

<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
  $(function () {
    var socket = io();
    $('form').submit(function(e){
      e.preventDefault(); // prevents page reloading
      socket.emit('chat message', $('#m').val());
      $('#m').val('');
      return false;
    });
    socket.on('chat message', function(msg){
      $('#messages').append($('<li>').text(msg));
    });
  });
</script>

完整的例子:https://socket.io/get-started/chat/

原答案:

某人(用户:https://stackoverflow.com/users/451634/benny-neugebauer |来自这篇文章:addEventListener on custom object)字面上给了我一个如何实现这一点的提示,除了快递之外没有任何其他包!我有它的工作!

首先,导入Node的EventEmitter:

const EventEmitter = require('events');

然后创建一个实例:

const Stream = new EventEmitter();

然后为事件流创建一个GET路由:

app.get('/stream', function(request, response){
  response.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  Stream.on("push", function(event, data) {
    response.write("event: " + String(event) + "\n" + "data: " + JSON.stringify(data) + "\n\n");
  });
});

在这个GET路由中,您要回写的是请求是200 OK,内容类型是文本/事件流,没有缓存,以及保持活动状态。

您还将调用EventEmitter实例的.on方法,它接受2个参数:要侦听的事件的字符串和处理该事件的函数(该函数可以获取与给定的一样多的参数)

现在....发送服务器事件所需要做的就是调用EventEmitter实例的.emit方法:

Stream.emit("push", "test", { msg: "admit one" });

第一个参数是要触发的事件的字符串(确保它与GET路由中的事件相同)。 .emit方法的每个后续参数都将传递给侦听器的回调!

这就对了!

由于您的实例是在路由定义上方的范围内定义的,因此您可以从任何其他路由调用.emit方法:

app.get('/', function(request, response){
  Stream.emit("push", "test", { msg: "admit one" });
  response.render("welcome.html", {});
});

感谢JavaScript作用域的工作方式,您甚至可以将EventEmitter实例传递给其他函数,甚至可以从其他模块传递:

const someModule = require('./someModule');

app.get('/', function(request, response){
  someModule.someMethod(request, Stream)
  .then(obj => { return response.json({}) });
});

一些人:

function someMethod(request, Stream) { 
  return new Promise((resolve, reject) => { 
    Stream.emit("push", "test", { data: 'some data' });
    return resolve();
  }) 
}

那简单!不需要其他包裹!

这是Node的EventEmitter类的链接:https://nodejs.org/api/events.html#events_class_eventemitter

我的例子:

const EventEmitter = require('events');
const express = require('express');
const app = express();

const Stream = new EventEmitter(); // my event emitter instance

app.get('/stream', function(request, response){
  response.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  Stream.on("push", function(event, data) {
    response.write("event: " + String(event) + "\n" + "data: " + JSON.stringify(data) + "\n\n");
  });
});

setInterval(function(){
  Stream.emit("push", "test", { msg: "admit one" });
}, 10000)

-2
投票

我说使用socket.io:https://www.npmjs.com/package/socket.io

它可以满足您的一切需求。服务器发送的事件,双向事件,频道等等!

© www.soinside.com 2019 - 2024. All rights reserved.