1. 概述

RESTful 服务可能会因各种原因失败。在这个教程中,我们将探讨如何在集成的 REST 服务抛出错误时,从 Feign 客户端获取原始消息。

2. Feign 客户端

Feign 是一个可插拔且声明式的 Web 服务客户端,它使编写 Web 服务客户端变得更加容易。除了 Feign 注解,它还支持 JAX-RS,并且提供了编码器和解码器,以提供更多的自定义选项。

3. 从 ErrorDecoder 中获取消息

当出现错误时,Feign 客户端会隐藏原始消息,为了获取它,我们需要编写一个自定义的 ErrorDecoder。如果没有这样的自定义,我们将会看到以下错误:

feign.FeignException$NotFound: [404] during [POST] to [http://localhost:8080/upload-error-1] [UploadClient#fileUploadError(MultipartFile)]: [{"timestamp":"2022-02-18T13:25:22.083+00:00","status":404,"error":"Not Found","path":"/upload-error-1"}]
    at feign.FeignException.clientErrorStatus(FeignException.java:219) ~[feign-core-11.7.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:194) ~[feign-core-11.7.jar:na]

为了处理这个错误,我们将创建一个简单的 ExceptionMessage Java 对象来表示错误消息:

public class ExceptionMessage {
    private String timestamp;
    private int status;
    private String error;
    private String message;
    private String path;
    // standard getters and setters
}

现在,让我们通过在自定义的 ErrorDecoder 实现中提取它来获取原始消息:

public class RetreiveMessageErrorDecoder implements ErrorDecoder {
    private ErrorDecoder errorDecoder = new Default();

    @Override
    public Exception decode(String methodKey, Response response) {
        ExceptionMessage message = null;
        try (InputStream bodyIs = response.body()
            .asInputStream()) {
            ObjectMapper mapper = new ObjectMapper();
            message = mapper.readValue(bodyIs, ExceptionMessage.class);
        } catch (IOException e) {
            return new Exception(e.getMessage());
        }
        switch (response.status()) {
        case 400:
            return new BadRequestException(message.getMessage() != null ? message.getMessage() : "Bad Request");
        case 404:
            return new NotFoundException(message.getMessage() != null ? message.getMessage() : "Not found");
        default:
            return errorDecoder.decode(methodKey, response);
        }
    }
}

在我们的实现中,我们基于可能的错误添加了逻辑,因此可以根据需要进行定制。在 switch 块的默认情况下,我们使用了 DefaultErrorDecoder 实现。

Default 实现会在状态不在 2xx 范围内时解码 HTTP 响应。如果 throwable 是可重试的(retryable),那么它应该是 RetryableException 的子类,并且我们应该尽可能地抛出应用特定的异常。

为了配置我们自定义的 ErrorDecoder,我们将在 Feign 配置中添加我们的实现作为 bean:

@Bean
public ErrorDecoder errorDecoder() {
    return new RetreiveMessageErrorDecoder();
}

现在,让我们看看带有原始消息的异常:

com.baeldung.cloud.openfeign.exception.NotFoundException: Page Not found
    at com.baeldung.cloud.openfeign.fileupload.config.RetreiveMessageErrorDecoder.decode(RetreiveMessageErrorDecoder.java:30) ~[classes/:na]
    at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:96) ~[feign-core-11.7.jar:na]

4. 总结

在这篇文章中,我们展示了如何定制 ErrorDecoder,以便我们可以捕获 Feign 错误并获取原始消息。

如往常一样,本教程中使用的所有代码示例都可以在 GitHub 上找到。


« 上一篇: Java Weekly, 第428期