Java Spring异步调用错误处理:如何通知React前端操作失败?
异步场景下通知前端操作失败的解决方案
你不需要退回到同步代码,异步执行时同样可以把错误信息传递给前端,下面是两种实用方案:
方案一:任务ID轮询(适合新手,实现简单)
核心思路是:API先返回一个任务ID给前端,前端定期查询任务状态,后端在异步执行完成后更新任务的成功/失败状态及错误信息。
1. 后端实现步骤
(1)创建任务状态实体
用来记录异步任务的执行情况:
@Entity public class TaskStatus { @Id @GeneratedValue(strategy = GenerationType.UUID) private String taskId; private String status; // 可选值:PENDING(处理中)、SUCCESS(成功)、FAILED(失败) private String errorMessage; private Long studentId; // 关联已创建的学生ID(成功时填充) // 省略getter、setter }
(2)修改API接口
返回任务ID,同时启动异步任务:
@PostMapping("/createStudent") public ResponseEntity<Map<String, String>> createStudent(@RequestBody Student student) { // 初始化任务状态为"处理中" TaskStatus task = new TaskStatus(); task.setStatus("PENDING"); taskStatusRepository.save(task); // 异步执行保存逻辑,传入任务ID studentService.createAsyncStudent(student, task.getTaskId()); // 返回任务ID给前端 return ResponseEntity.ok(Map.of("taskId", task.getTaskId())); } // 新增查询任务状态的API @GetMapping("/task/{taskId}") public ResponseEntity<TaskStatus> getTaskStatus(@PathVariable String taskId) { TaskStatus task = taskStatusRepository.findById(taskId) .orElseThrow(() -> new RuntimeException("任务不存在")); return ResponseEntity.ok(task); }
(3)异步服务方法更新任务状态
@Async public void createAsyncStudent(Student student, String taskId) { TaskStatus task = taskStatusRepository.findById(taskId).get(); try { // 原有的学生保存逻辑 Student createdStudent = studentRepository.save(student); task.setStatus("SUCCESS"); task.setStudentId(createdStudent.getId()); } catch (Exception e) { // 捕获错误,更新任务状态和错误信息 task.setStatus("FAILED"); task.setErrorMessage(e.getMessage()); } finally { taskStatusRepository.save(task); } }
2. 前端React逻辑
拿到任务ID后定时查询状态:
async function submitStudentForm(studentData) { // 提交创建请求 const createRes = await fetch('/createStudent', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(studentData) }); const { taskId } = await createRes.json(); // 轮询查询任务状态 const statusInterval = setInterval(async () => { const statusRes = await fetch(`/task/${taskId}`); const taskStatus = await statusRes.json(); if (taskStatus.status === 'SUCCESS') { clearInterval(statusInterval); alert(`学生创建成功,ID:${taskStatus.studentId}`); } else if (taskStatus.status === 'FAILED') { clearInterval(statusInterval); alert(`创建失败:${taskStatus.errorMessage}`); } }, 1000); // 每秒查询一次,可根据实际调整间隔 }
方案二:WebSocket主动推送(实时性更强)
核心思路是:前端和后端建立WebSocket连接,后端异步任务出错时,主动向对应的前端推送错误信息。
1. 后端WebSocket配置
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // 注册WebSocket处理器,允许跨域 registry.addHandler(new TaskErrorHandler(), "/task-notify").setAllowedOrigins("*"); } } // WebSocket消息处理器 public class TaskErrorHandler extends TextWebSocketHandler { // 存储客户端会话,key为会话ID private static final Map<String, WebSocketSession> clientSessions = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) { clientSessions.put(session.getId(), session); } // 提供静态方法,向指定客户端推送错误信息 public static void pushError(String sessionId, String errorMsg) { WebSocketSession session = clientSessions.get(sessionId); if (session != null && session.isOpen()) { try { session.sendMessage(new TextMessage(errorMsg)); } catch (IOException e) { e.printStackTrace(); } } } }
2. 修改创建学生API
@PostMapping("/createStudent") public ResponseEntity<Void> createStudent(@RequestBody Student student, HttpServletRequest request) { // 获取当前请求的会话ID,用来定位推送的客户端 String sessionId = request.getSession().getId(); studentService.createAsyncStudent(student, sessionId); return ResponseEntity.ok().build(); }
3. 异步服务方法推送错误
@Async public void createAsyncStudent(Student student, String sessionId) { try { studentRepository.save(student); } catch (Exception e) { // 向前端推送错误信息 TaskErrorHandler.pushError(sessionId, "学生创建失败:" + e.getMessage()); } }
4. 前端WebSocket连接逻辑
useEffect(() => { // 建立WebSocket连接 const socket = new WebSocket('ws://localhost:8080/task-notify'); // 接收后端推送的消息 socket.onmessage = (event) => { alert(`后端通知:${event.data}`); }; // 组件卸载时关闭连接 return () => socket.close(); }, []);
注意事项
- 轮询方案要注意清理过期的任务状态数据,避免数据库冗余。
- WebSocket方案需要处理连接断开重连的情况,确保消息不丢失。
- 两种方案都可以配合全局异常处理器,统一捕获异步任务中的异常。
内容的提问来源于stack exchange,提问作者random55645
相关产品推荐
相关产品推荐

