我正在使用 Spring 的
SseEmitter
实现与 SSE 合作。
就我而言,我想在服务器端找不到项目时处理错误并发送带有错误消息的事件。
我的第一个想法是从服务的方法中抛出异常,然后在用
@ExceptionHandler
注释的类中通过 @ControllerAdvice
处理它。这不起作用,因为 @ControllerAdvice
类对 SSE 客户端一无所知。
之后我尝试了以下代码:
private void sendError(String message, int status) {
log.error("Processing report {} stopped with error '{}'", getContextLogMessage(), message);
sseEmitter.completeWithError(new ApiError(message, HttpStatus.resolve(status)));
sseEmitter.onCompletion(this::stopSelf);
}
但是SSE客户端收到了下一条消息:
Received error
Event { type: 'error', status: 500, message: '' }
看起来 Spring 的默认错误消息已传递到 SSE 客户端。
我的SSE客户代码:
const EventSource = require('eventsource')
const eventSource = new EventSource('http://localhost:8080/testing')
eventSource.onmessage = (e) => {
const data = JSON.parse(e.data)
if (data.status == null) {
console.log(data)
} else if (data.status === 'DONE') {
console.log(data.status);
eventSource.close()
} else {
console.log('status = ' + data.status)
}
}
eventSource.onerror = (e) => {
console.log('Received error')
console.log(e)
eventSource.close()
}
我的问题是 - 是否有可能通过
@ExceptionHandler
处理它?也许我对Spring的SSE有什么误解,我以前只使用过sseEmitter.send()
。
我也有同样的情况,即我想继续使用我已经在使用的@ControllerAdvice和@ExceptionHandler。 由于我的 REST 调用返回 SseEmitter 并生成文本/事件流,因此我现有的 ExceptionHandlers 将无法工作,因为它们返回 json。 所以我就做了以下事情。
@ControllerAdvice
public class MyExceptionHandlerClass{
@ExceptionHandler(MyException.class)
public final ResponseEntity<Object> handleMyException(MyException ex){
return ResponseEntity.status(ex.getStatus())
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_EVENT_STREAM)
.body(ex.getMessage);
}
}
除了jack的回答之外,还可以取得一些进展。
event: error
data: some description to the error
客户端可以收到错误事件:
eventSource.addEventListener("error", (event) => {
console.log(event.data);
});
annotations
中的 ControllerAdvice
字段。您可能需要先创建自定义注释并将其添加到控制器中。所以我的完整解决方案是:
@ControllerAdvice(annotations = MySseResponseController.class)
@Slf4j
public class SseErrorHandler {
@ExceptionHandler(Exception.class)
public void handleStreamException(HttpServletResponse response, Exception ex) {
log.error("sse request handling failed", ex);
if (!response.isCommitted()) {
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
response.setContentType("text/event-stream");
}
PrintWriter output = response.getWriter();
output.write("event: error\ndata: " + ex.getMessage() + "\n\n");
output.close();
}
}