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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:42:45