在生成的事件流值中找不到 Spring 的 SSEmitter 事件 "name" 属性
Cannot find Spring's SSEmitter Event "name" property in the resulting event-stream value
我在 Spring 框架内使用服务器发送事件 (SSE)。我可以在 chrome 浏览器和后端使用 jersey-media-sse
中使用 javascript 代码注册到这些流
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-sse</artifactId>
<version>2.21</version>
</dependency>
现在的问题是 Spring 的 SSEmitter 逻辑没有按预期设置事件的 name 属性。在下面的发送逻辑中使用 setter 似乎很直观 - 但 consuming/receiving 客户端的结果值会产生 name:null。该属性仍然可用 - 但很难在大 JSON 字符串的三个 data 字段之一中找到。
请阅读以下内容以详细了解问题
这是应该产生 SSEvent 流的端点片段
@CrossOrigin(Resources.Origins.FRONTEND)
@GetMapping(GET_LOG + "/stream")
public SseEmitter subscribeToLogEventStream() {
Optional<SseEmitter> sseEmitter = createAndSubscribe(EVENT_IDENTIFIER);
return sseEmitter.orElse(null);
}
发送事件的逻辑如下
void sendEvent(SseEmitter sseEmitter, String toolId, String message) {
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
.data(message, MediaType.TEXT_EVENT_STREAM)
.name(toolId);
Set<ResponseBodyEmitter.DataWithMediaType> event = sseEventBuilder.build();
try {
sseEmitter.send(event);
log.debug("Sending SSEvent");
} catch (ClientAbortException e) {
log.error("Stream connection is not longer established. Aborting.", e);
} catch (IOException e) {
log.error("Server sent event emitter failed", e);
}
}
在后端接收此事件,InboundEvent 对象产生这些属性
请注意 name 字段恰好为空,尽管它已在发送逻辑中设置
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
.data(message, MediaType.TEXT_EVENT_STREAM)
.name(toolId);
实际上 name 字段是在以下 JSON 字符串的 3rd 数据字段中定义的。
奇怪格式的事件流数据
[
{
"data": "data:",
"mediaType": {
"type": "text",
"subtype": "plain",
"parameters": {
"charset": "UTF-8"
},
"qualityValue": 1.0,
"concrete": true,
"wildcardType": false,
"charSet": "UTF-8",
"charset": "UTF-8",
"wildcardSubtype": false
}
},
{
"data": "Wed Jan 24 18:17:13 CET 2018>ERROR: Finishing execution but still unhappy",
"mediaType": {
"type": "text",
"subtype": "event-stream",
"parameters": {},
"qualityValue": 1.0,
"concrete": true,
"wildcardType": false,
"charSet": null,
"charset": null,
"wildcardSubtype": false
}
},
{
"data": "\nevent:foo\n\n", // <<<< Name definition (suffixed with event)
"mediaType": {
"type": "text",
"subtype": "plain",
"parameters": {
"charset": "UTF-8"
},
"qualityValue": 1.0,
"concrete": true,
"wildcardType": false,
"charSet": "UTF-8",
"charset": "UTF-8",
"wildcardSubtype": false
}
}
]
我刚刚发现 .send()
方法不需要 SseEmitter.SseEventBuilder
创建的事件 - 不,它需要构建器本身。这对我来说有点不直观,因此很长一段时间都没有意识到。
void sendEvent(SseEmitter sseEmitter, String toolId, String message) {
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
.data(message)
.name(toolId);
try {
sseEmitter.send(sseEventBuilder );
log.debug("Sending SSEvent");
} catch (ClientAbortException e) {
log.error("Stream connection is not longer established. Aborting.", e);
} catch (IOException e) {
log.error("Server sent event emitter failed", e);
}
}
我在 Spring 框架内使用服务器发送事件 (SSE)。我可以在 chrome 浏览器和后端使用 jersey-media-sse
中使用 javascript 代码注册到这些流 <dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-sse</artifactId>
<version>2.21</version>
</dependency>
现在的问题是 Spring 的 SSEmitter 逻辑没有按预期设置事件的 name 属性。在下面的发送逻辑中使用 setter 似乎很直观 - 但 consuming/receiving 客户端的结果值会产生 name:null。该属性仍然可用 - 但很难在大 JSON 字符串的三个 data 字段之一中找到。
请阅读以下内容以详细了解问题
这是应该产生 SSEvent 流的端点片段
@CrossOrigin(Resources.Origins.FRONTEND)
@GetMapping(GET_LOG + "/stream")
public SseEmitter subscribeToLogEventStream() {
Optional<SseEmitter> sseEmitter = createAndSubscribe(EVENT_IDENTIFIER);
return sseEmitter.orElse(null);
}
发送事件的逻辑如下
void sendEvent(SseEmitter sseEmitter, String toolId, String message) {
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
.data(message, MediaType.TEXT_EVENT_STREAM)
.name(toolId);
Set<ResponseBodyEmitter.DataWithMediaType> event = sseEventBuilder.build();
try {
sseEmitter.send(event);
log.debug("Sending SSEvent");
} catch (ClientAbortException e) {
log.error("Stream connection is not longer established. Aborting.", e);
} catch (IOException e) {
log.error("Server sent event emitter failed", e);
}
}
在后端接收此事件,InboundEvent 对象产生这些属性
请注意 name 字段恰好为空,尽管它已在发送逻辑中设置
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
.data(message, MediaType.TEXT_EVENT_STREAM)
.name(toolId);
实际上 name 字段是在以下 JSON 字符串的 3rd 数据字段中定义的。
奇怪格式的事件流数据
[
{
"data": "data:",
"mediaType": {
"type": "text",
"subtype": "plain",
"parameters": {
"charset": "UTF-8"
},
"qualityValue": 1.0,
"concrete": true,
"wildcardType": false,
"charSet": "UTF-8",
"charset": "UTF-8",
"wildcardSubtype": false
}
},
{
"data": "Wed Jan 24 18:17:13 CET 2018>ERROR: Finishing execution but still unhappy",
"mediaType": {
"type": "text",
"subtype": "event-stream",
"parameters": {},
"qualityValue": 1.0,
"concrete": true,
"wildcardType": false,
"charSet": null,
"charset": null,
"wildcardSubtype": false
}
},
{
"data": "\nevent:foo\n\n", // <<<< Name definition (suffixed with event)
"mediaType": {
"type": "text",
"subtype": "plain",
"parameters": {
"charset": "UTF-8"
},
"qualityValue": 1.0,
"concrete": true,
"wildcardType": false,
"charSet": "UTF-8",
"charset": "UTF-8",
"wildcardSubtype": false
}
}
]
我刚刚发现 .send()
方法不需要 SseEmitter.SseEventBuilder
创建的事件 - 不,它需要构建器本身。这对我来说有点不直观,因此很长一段时间都没有意识到。
void sendEvent(SseEmitter sseEmitter, String toolId, String message) {
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
.data(message)
.name(toolId);
try {
sseEmitter.send(sseEventBuilder );
log.debug("Sending SSEvent");
} catch (ClientAbortException e) {
log.error("Stream connection is not longer established. Aborting.", e);
} catch (IOException e) {
log.error("Server sent event emitter failed", e);
}
}