如何处理异常并向SSE客户端发送错误

问题描述 投票:0回答:2

我正在使用 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()

java spring spring-boot exception server-sent-events
2个回答
0
投票

我也有同样的情况,即我想继续使用我已经在使用的@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);
    }
}

0
投票

除了jack的回答之外,还可以取得一些进展。

  1. 正文应该是有效的 sse 事件。 错误可以用这种格式编码:
event: error
data: some description to the error

客户端可以收到错误事件:

eventSource.addEventListener("error", (event) => {
  console.log(event.data);
});

参考

  1. 您可能还有一些其他返回正常响应的 API,例如 json 或 html。此控制器建议将适用于所有控制器。 为了限制范围,可以使用
    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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.