如何在Spring Integration Java DSL中自定义错误响应
问题分析与解决方案
首先,你的代码出现ClassCastException的核心原因是:当目标API宕机时,Http.outboundGateway抛出的是ResourceAccessException(连接超时/拒绝连接等网络异常的包装类),但你的错误处理流强制将其转换为HttpClientErrorException,导致类型转换失败。同时,当前错误处理返回的RuntimeException会触发Spring默认错误处理器,返回通用的500错误响应。
以下是实现自定义异常响应的分步解决方案:
1. 修复类型转换异常
首先需要在错误处理流中区分不同的异常类型,避免强制转换:
步骤1:创建自定义错误响应DTO
定义一个用于返回给客户端的结构化错误响应类:
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class CustomErrorResponse { private String timestamp; private int status; private String error; private String message; private String path; public CustomErrorResponse(int status, String error, String message, String path) { this.timestamp = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME); this.status = status; this.error = error; this.message = message; this.path = path; } // Getter方法(用于JSON序列化) public String getTimestamp() { return timestamp; } public int getStatus() { return status; } public String getError() { return error; } public String getMessage() { return message; } public String getPath() { return path; } }
步骤2:重构错误处理流
修改httpProxyErrorFlow,处理不同异常类型并返回自定义响应:
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.transformer.Transformers; import org.springframework.messaging.MessagingException; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.ResourceAccessException; @Bean public IntegrationFlow httpProxyErrorFlow() { return f -> f // 提取原始异常(处理MessagingException包装的情况) .transform(payload -> { if (payload instanceof MessagingException) { return ((MessagingException) payload).getCause(); } return payload; }) // 根据异常类型生成自定义响应 .handle((payload, headers) -> { // 从请求头获取原始请求路径 String path = headers.getOrDefault(HttpHeaders.REQUEST_URL, "/narko/api/patient/by-pinpp").toString(); CustomErrorResponse errorResponse; HttpStatus status; if (payload instanceof ResourceAccessException) { // 处理目标服务宕机、连接失败等情况 status = HttpStatus.SERVICE_UNAVAILABLE; errorResponse = new CustomErrorResponse( status.value(), status.getReasonPhrase(), "目标服务暂时不可用,请稍后重试", path ); } else if (payload instanceof HttpClientErrorException) { // 处理目标服务返回的4xx错误 HttpClientErrorException ex = (HttpClientErrorException) payload; status = ex.getStatusCode(); errorResponse = new CustomErrorResponse( status.value(), status.getReasonPhrase(), "请求目标服务失败:" + ex.getMessage(), path ); } else { // 处理其他未知异常 status = HttpStatus.INTERNAL_SERVER_ERROR; errorResponse = new CustomErrorResponse( status.value(), status.getReasonPhrase(), "服务器内部错误", path ); } // 构建包含状态码的响应消息 return MessageBuilder.withPayload(errorResponse) .setHeader(HttpHeaders.STATUS_CODE, status) .build(); }) // 将响应对象序列化为JSON .transform(Transformers.toJson()); }
2. 修正其他潜在问题
- 修正
main方法中的类名错误(当前代码中类是IntegrationApp,但启动的是MinzdravApplication):
public static void main(String[] args) { SpringApplication.run(IntegrationApp.class, args); }
3. 效果验证
当目标API宕机时,客户端会收到如下自定义响应:
{ "timestamp": "2022-08-10T15:30:00.123+08:00", "status": 503, "error": "Service Unavailable", "message": "目标服务暂时不可用,请稍后重试", "path": "/narko/api/patient/by-pinpp" }
同时日志中不会再出现ClassCastException异常。
内容的提问来源于stack exchange,提问作者Frank
相关产品推荐
相关产品推荐

