You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Spring SseEmitter时如何处理异常并向SSE客户端发送错误信息

问题1:是否可以通过@ExceptionHandler处理SSE场景的异常?

常规的@ControllerAdvice+@ExceptionHandler方案只能捕获控制器方法执行过程中同步抛出的异常,如果你的控制器已经返回了SseEmitter对象,后续的业务逻辑是在独立的异步线程中执行的,已经脱离了原请求的处理上下文,@ControllerAdvice无法感知到这个线程抛出的异常,因此不能直接用原生机制处理该场景的异步异常。

如果要复用全局异常处理的逻辑,可以做如下适配:

  • 将@ExceptionHandler中的异常转换逻辑封装为公共的工具方法
  • 在SSE异步业务逻辑的最外层加统一try-catch,捕获到异常后调用上述公共方法得到标准化的错误对象,再通过SSE通道发送给客户端

如果异常是在控制器返回SseEmitter之前抛出的(比如参数校验失败、项目不存在的判断在同步逻辑中),此时SSE连接还未建立,@ExceptionHandler可以正常捕获异常,直接返回带错误信息的HTTP响应即可。

问题2:Spring SSE的使用误区说明

你现在调用completeWithError()方法返回空错误信息是符合Spring的设计逻辑的,该方法的作用是直接中断SSE连接、返回对应HTTP错误状态码,不会将你传入的异常对象序列化后发送给客户端,浏览器的EventSource的onerror回调只能拿到状态码,无法获取自定义错误内容。

正确的自定义错误发送逻辑是:将错误信息作为普通SSE事件发送,发送完成后再正常结束连接,代码示例如下:

服务端修改

private void sendError(String message, int status) {
    log.error("Processing report {} stopped with error '{}'", getContextLogMessage(), message);
    try {
        // 构造自定义错误事件发送给客户端
        sseEmitter.send(SseEmitter.event()
                // 指定事件名,方便前端针对性监听
                .name("custom_error")
                .data(new ApiError(message, HttpStatus.resolve(status)))
                .build());
    } catch (IOException e) {
        // 处理发送失败场景,一般为客户端提前断开连接
        log.error("Failed to send error event to client", e);
    } finally {
        // 正常结束连接,不要用completeWithError
        sseEmitter.complete();
        sseEmitter.onCompletion(this::stopSelf);
    }
}

客户端修改

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.addEventListener('custom_error', (e) => {
  const error = JSON.parse(e.data)
  console.log('收到服务端错误', error)
  eventSource.close()
})

// 原onerror保留,用于处理底层连接错误、网络异常等场景
eventSource.onerror = (e) => {
  console.log('连接异常', e)
  eventSource.close()
}

内容的提问来源于stack exchange,提问作者NikMashei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 05:57:00