基于Clean Architecture的Java项目:外部API异常如何反馈至客户端?
如何将外部API失败结果反馈至客户端(Clean Architecture Java项目)
在Clean Architecture架构下,要把外部API的失败结果传递到客户端,核心是通过自定义业务异常打通各层的错误传递路径,再通过全局异常处理器统一返回标准化响应。具体实现步骤如下:
1. 定义自定义业务异常类
创建继承自RuntimeException的业务异常类,封装外部API调用失败的关键信息,方便跨层传递:
public class ExternalApiFailureException extends RuntimeException { private final int externalStatusCode; private final String errorCode; public ExternalApiFailureException(String message, int externalStatusCode, String errorCode) { super(message); this.externalStatusCode = externalStatusCode; this.errorCode = errorCode; } // Getter方法 public int getExternalStatusCode() { return externalStatusCode; } public String getErrorCode() { return errorCode; } }
如果需要区分不同类型的外部错误(比如超时、权限不足),可以再定义该类的子类,比如ExternalApiTimeoutException、ExternalApiForbiddenException。
2. 在Adapter Out层捕获外部API异常并抛出业务异常
在外部网关实现中,捕获RestTemplate调用时抛出的网络或HTTP异常,将其包装为自定义业务异常抛出:
@Override public void updateInfo(String info) { try { // 原RestTemplate调用逻辑 restTemplate.postForObject("external-api-url", info, Void.class); } catch (HttpStatusCodeException e) { // 处理HTTP状态码异常(比如400、404、500) throw new ExternalApiFailureException( "外部API更新失败: " + e.getResponseBodyAsString(), e.getStatusCode().value(), "EXTERNAL_API_ERROR" ); } catch (ResourceAccessException e) { // 处理连接超时、无法连接等网络异常 throw new ExternalApiFailureException( "无法连接外部API: " + e.getMessage(), 0, // 无HTTP状态码时设为0 "EXTERNAL_API_CONNECTION_ERROR" ); } }
3. 应用层(UseCase)无需额外处理
updateInfoUseCase.updateInfo()方法不需要捕获异常,直接让异常向上传递到Controller层即可——这符合Clean Architecture的依赖规则,应用层不依赖外部实现细节,只关注业务逻辑。
4. Controller层全局处理异常,返回标准化响应
创建全局异常处理器,捕获自定义业务异常,转换为客户端可识别的HTTP响应:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ExternalApiFailureException.class) public ResponseEntity<ErrorResponse> handleExternalApiFailure(ExternalApiFailureException e) { ErrorResponse errorResponse = new ErrorResponse( e.getErrorCode(), e.getMessage() ); // 根据外部API状态码或错误类型设置HTTP响应码 HttpStatus status = e.getExternalStatusCode() != 0 ? HttpStatus.valueOf(e.getExternalStatusCode()) : HttpStatus.SERVICE_UNAVAILABLE; return new ResponseEntity<>(errorResponse, status); } // 可选:处理其他全局异常 @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleGenericException(Exception e) { ErrorResponse errorResponse = new ErrorResponse( "INTERNAL_ERROR", "服务器内部错误" ); return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } } // 错误响应DTO public class ErrorResponse { private String errorCode; private String message; // 构造方法、Getter public ErrorResponse(String errorCode, String message) { this.errorCode = errorCode; this.message = message; } public String getErrorCode() { return errorCode; } public String getMessage() { return message; } }
同时,修改Controller方法,返回成功响应(可选,不影响异常处理,但能让正常请求有明确响应):
@PostMapping(path = "/update/info") public ResponseEntity<Void> updateInfo(@RequestBody Info request) { Info info = new Info(request.getXXX()); updateInfoUseCase.updateInfo(info); return ResponseEntity.ok().build(); }
5. 可选:增强错误信息粒度
如果需要给客户端更精准的错误提示,可以扩展自定义异常的枚举类型:
public enum ExternalApiError { TIMEOUT("EXTERNAL_TIMEOUT", "外部API请求超时"), FORBIDDEN("EXTERNAL_FORBIDDEN", "无权限访问外部API"), NOT_FOUND("EXTERNAL_NOT_FOUND", "外部API资源不存在"), UNKNOWN("EXTERNAL_UNKNOWN", "外部API调用失败"); private final String code; private final String message; // 构造方法、Getter }
抛出异常时使用枚举,让响应的错误码和消息更标准化。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

