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

如何在Spring Boot Webflux SSE端点向Angular 7传递错误信息

解决Webflux SSE端点异常信息无法传递到Angular前端的问题

我之前也踩过这个坑!核心问题在于EventSource本身的设计限制:当服务端返回非2xx的HTTP状态码时,EventSource只会触发全局的error事件,但无法获取响应的状态码和响应体——它直接就断开连接了,根本不给你读取错误信息的机会。所以你不能像常规REST端点那样直接抛出带@ResponseStatus的异常,得换一种方式在SSE流里传递错误信息。

下面是两种可行的解决方案,你可以根据自己的架构选择:

方案1:用自定义事件传递错误信息

这种方式是在SSE流里发送一个专门的"错误事件",把状态码和异常详情打包进去,让前端通过监听这个事件来处理错误。

后端代码调整

你需要捕获服务抛出的异常,把它转换成一个带有自定义类型的ServerSentEvent,而不是直接抛出异常中断流:

@GetMapping(path = "{id}/async", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<Object>> getProcessedResultStream(@PathVariable("id") String id) {
    return Mono.fromCallable(() -> service.getProcessedResult(id))
        // 处理正常结果,发送普通消息事件
        .map(result -> ServerSentEvent.<Object>builder()
            .data(Optional.of(result))
            .build())
        // 捕获自定义异常,转换为错误事件
        .onErrorResume(RequestNotFoundException.class, ex -> 
            Flux.just(ServerSentEvent.<Object>builder()
                .event("error-event") // 用自定义事件名避免和EventSource全局error冲突
                .data(Map.of(
                    "status", HttpStatus.NOT_FOUND.value(),
                    "message", ex.getMessage()
                ))
                .build())
        )
        // 最后发送断开连接事件
        .concatWith(Flux.just(ServerSentEvent.builder()
            .event("disconnect")
            .build()));
}

前端Angular处理

在前端,你需要监听这个自定义的error-event事件,解析里面的错误信息,同时保留原有的断开连接和全局错误处理:

const eventSource = new EventSource(`/your-api-path/${id}/async`);

// 处理正常结果
eventSource.addEventListener('message', (event) => {
  const result = JSON.parse(event.data);
  console.log('处理结果:', result);
  // 这里更新你的UI逻辑
});

// 处理自定义错误事件
eventSource.addEventListener('error-event', (event) => {
  const errorData = JSON.parse(event.data);
  console.error(`请求失败 [${errorData.status}]: ${errorData.message}`);
  // 显示错误提示给用户
  eventSource.close(); // 错误后主动关闭连接
});

// 处理连接级别的错误(比如网络中断)
eventSource.addEventListener('error', (event) => {
  console.error('SSE连接异常:', event);
  eventSource.close();
});

// 处理断开连接事件
eventSource.addEventListener('disconnect', () => {
  eventSource.close();
  console.log('数据流已断开');
});

方案2:用统一响应DTO包装结果和错误

如果你的项目有统一的响应格式,也可以创建一个通用的DTO来包装正常结果和错误信息,这样前端只需要解析同一个结构即可,不需要区分不同的事件类型。

第一步:创建统一响应DTO

public class SseResponse<T> {
    private boolean success;
    private T data;
    private Integer statusCode;
    private String errorMessage;

    // 构造器:正常结果
    public SseResponse(T data) {
        this.success = true;
        this.data = data;
        this.statusCode = null;
        this.errorMessage = null;
    }

    // 构造器:错误结果
    public SseResponse(Integer statusCode, String errorMessage) {
        this.success = false;
        this.data = null;
        this.statusCode = statusCode;
        this.errorMessage = errorMessage;
    }

    // Getter和Setter省略
}

第二步:后端返回包装后的响应

@GetMapping(path = "{id}/async", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<SseResponse<ProcessedResult>>> getProcessedResultStream(@PathVariable("id") String id) {
    return Mono.fromCallable(() -> service.getProcessedResult(id))
        .map(SseResponse::new) // 正常结果包装
        .map(response -> ServerSentEvent.builder(response).build())
        .onErrorResume(RequestNotFoundException.class, ex -> 
            Flux.just(ServerSentEvent.builder(
                new SseResponse<>(HttpStatus.NOT_FOUND.value(), ex.getMessage())
            ).build())
        )
        .concatWith(Flux.just(ServerSentEvent.builder(new SseResponse<>(null))
            .event("disconnect")
            .build()));
}

第三步:前端统一解析

eventSource.addEventListener('message', (event) => {
  const response: SseResponse<ProcessedResult> = JSON.parse(event.data);
  if (response.success) {
    if (response.data) {
      // 处理正常结果
      console.log('处理结果:', response.data);
    }
  } else {
    // 处理错误
    console.error(`请求失败 [${response.statusCode}]: ${response.errorMessage}`);
    eventSource.close();
  }
});

// 断开连接和全局错误处理同上

为什么不能直接抛出异常?

再强调一下:SSE是基于持续HTTP流的协议,一旦服务端返回非2xx状态码,EventSource会立即终止连接,并且不会暴露响应体内容——这是EventSource的标准行为,不是Angular或Webflux的问题。所以必须在流的内部传递错误信息,而不是通过HTTP状态码。

如果你的场景对HTTP状态码有强需求,那可能需要考虑替换成WebSocket,它的双向通信模型能更好地处理错误状态传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:36:21