Koa Server-sent Events 只对浏览器中的 EventSource 产生错误
Koa Server-sent Events yields only errors to EventSource in browser
我有一个 Koa 节点服务器,我正试图从中发送服务器发送的事件。但是,我只看到 EventSource.onerror
回调在事件到达时被调用。
这是我的 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 路由处理器:
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 );
}
当我尝试在浏览器中使用这些事件时:
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() );
};
这个输出是
Opened: Event {isTrusted: true, type: "open", target: EventSource, currentTarget: EventSource, eventPhase: 2, …}
ReadyState 1
An error occurred: Event {isTrusted: true, type: "error", target: EventSource, currentTarget: EventSource, eventPhase: 2, …}
ReadyState 0
Opened: Event {isTrusted: true, type: "open", target: EventSource, currentTarget:
EventSource, eventPhase: 2, …}
ReadyState 1
An error occurred: Event {isTrusted: true, type: "error", target: EventSource, currentTarget: EventSource, eventPhase: 2, …}
ReadyState 0
...
通过将流对象正确分配给 Koa ctx.body
对象,我能够开始成功处理事件。
static buildStreamContext( ctx: Koa.Context ): KoaServerSentStream {
// ...
ctx.body = stream;
ctx.type = 'text/event-stream';
// ...
}
应该是:
static buildStreamContext( ctx: Koa.Context ): KoaServerSentStream {
// ...
ctx.body = stream.eventStream;
ctx.type = 'text/event-stream';
// ...
}
希望这可以帮助到别人!
我有一个 Koa 节点服务器,我正试图从中发送服务器发送的事件。但是,我只看到 EventSource.onerror
回调在事件到达时被调用。
这是我的 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 路由处理器:
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 );
}
当我尝试在浏览器中使用这些事件时:
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() );
};
这个输出是
Opened: Event {isTrusted: true, type: "open", target: EventSource, currentTarget: EventSource, eventPhase: 2, …}
ReadyState 1
An error occurred: Event {isTrusted: true, type: "error", target: EventSource, currentTarget: EventSource, eventPhase: 2, …}
ReadyState 0
Opened: Event {isTrusted: true, type: "open", target: EventSource, currentTarget: EventSource, eventPhase: 2, …}
ReadyState 1
An error occurred: Event {isTrusted: true, type: "error", target: EventSource, currentTarget: EventSource, eventPhase: 2, …}
ReadyState 0
...
通过将流对象正确分配给 Koa ctx.body
对象,我能够开始成功处理事件。
static buildStreamContext( ctx: Koa.Context ): KoaServerSentStream {
// ...
ctx.body = stream;
ctx.type = 'text/event-stream';
// ...
}
应该是:
static buildStreamContext( ctx: Koa.Context ): KoaServerSentStream {
// ...
ctx.body = stream.eventStream;
ctx.type = 'text/event-stream';
// ...
}
希望这可以帮助到别人!