简体   繁体   English

Koa Server-sent Events仅向浏览器中的EventSource产生错误

[英]Koa Server-sent Events yields only errors to EventSource in browser

I've got a Koa Node server which I'm trying to send server-sent events from. 我有一个Koa Node服务器,我正在尝试从中发送服务器发送的事件。 However I'm only seeing the EventSource.onerror callback being invoked when the events arrive. 但是我只看到事件到达时调用的EventSource.onerror回调。

Here is my SSE implementation: 这是我的SSE实现:

import * as Koa from 'koa';
import { PassThrough } from 'stream';
import { KoaServerSentEvent } from './koaServerSentEvent';

export class KoaServerSentStream {
  private readonly eventStream: PassThrough;
  private readonly retryInterval: number;
  private eventCount: number;
  readonly request: Koa.Request;
  readonly response: Koa.Response;

  constructor( req: Koa.Request, resp: Koa.Response ) {
    this.retryInterval = 10000;
    this.eventCount = 0;
    this.eventStream = new PassThrough();
    this.request = req;
    this.response = resp;
    this.configureLifecycle();
  }

  static buildStreamContext( ctx: Koa.Context ): KoaServerSentStream {
    const stream: KoaServerSentStream = new KoaServerSentStream( ctx.request, ctx.response );
    // order matters here: https://github.com/koajs/koa/issues/1120
    ctx.body = stream;
    ctx.type = 'text/event-stream';
    ///////////////////////////////////////////////////////////////
    return stream;
  }

  private configureLifecycle(): void {
    this.request.req.on( 'close', this.response.res.end );
    this.request.req.on( 'finish', this.response.res.end );
    this.request.req.on( 'error', this.response.res.end );
    this.eventStream.write( `retry: ${this.retryInterval}` );
  }

  queueEvent( event: KoaServerSentEvent ): void {
    const formattedData: string = ( typeof event.data === 'string' ) ? event.data : JSON.stringify( event.data );
    const payload: string = `id: ${this.eventCount}\nevent: ${event.name}\ndata: ${formattedData}`;
    this.eventStream.write( payload );
    this.eventCount++;
  }

  publish(): void {
    this.eventStream.write( '\n\n' );
  }
}

POC RouteHandler: POC RouteHandler:

export async function handler( ctx: Koa.Context ): Promise<void> {
  const stream: KoaServerSentStream = KoaServerSentStream.buildStreamContext( ctx );
  setInterval( () => {
    stream.queueEvent( {
      name: 'greetings',
      data: {
        french: 'bonjour tout le monde',
        english: 'hello world',
        german: 'hallo welt',
        spanish: 'hola mundo'
      }
    } );
    stream.publish();
  }, 2000 );
}

When I try to consume these events in a browser using: 当我尝试在浏览器中使用以下内容消费这些事件时:

            const events = new EventSource( 'http://localhost:3000/v1/helloWorld' );
            events.onmessage = ( e ) => {
              console.log( 'We received an event!', e );
            };

            events.onerror = ( error ) => {
              console.error( 'An error occurred:', error );
              console.info( 'ReadyState', events.readyState.valueOf() );
              // events.close();
            };

            events.onopen = ( opened ) => {
              console.info( 'Opened:', opened );
              console.info( 'ReadyState', events.readyState.valueOf() );
            };

The output from this is 这个输出是

Opened: Event {isTrusted: true, type: "open", target: EventSource, currentTarget: EventSource, eventPhase: 2, …} 已打开:事件{isTrusted:true,type:“open”,target:EventSource,currentTarget:EventSource,eventPhase:2,...}

ReadyState 1 ReadyState 1

An error occurred: Event {isTrusted: true, type: "error", target: EventSource, currentTarget: EventSource, eventPhase: 2, …} 发生错误:事件{isTrusted:true,type:“error”,target:EventSource,currentTarget:EventSource,eventPhase:2,...}

ReadyState 0 ReadyState 0

Opened: Event {isTrusted: true, type: "open", target: EventSource, currentTarget: EventSource, eventPhase: 2, …} 已打开:事件{isTrusted:true,type:“open”,target:EventSource,currentTarget:EventSource,eventPhase:2,...}

ReadyState 1 ReadyState 1

An error occurred: Event {isTrusted: true, type: "error", target: EventSource, currentTarget: EventSource, eventPhase: 2, …} 发生错误:事件{isTrusted:true,type:“error”,target:EventSource,currentTarget:EventSource,eventPhase:2,...}

ReadyState 0 ReadyState 0

... ...

I was able to start successfully processing events by properly assigning the stream object to the Koa ctx.body object. 通过将流对象正确分配给Koa ctx.body对象,我能够成功处理事件。

static buildStreamContext( ctx: Koa.Context ): KoaServerSentStream {
    // ...
    ctx.body = stream;
    ctx.type = 'text/event-stream';
    // ...
  }

should have been: 应该是:

static buildStreamContext( ctx: Koa.Context ): KoaServerSentStream {
    // ...
    ctx.body = stream.eventStream;
    ctx.type = 'text/event-stream';
    // ...
  }

Hopefully this can help someone! 希望这可以帮助别人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM