Feign Client 异常处理

1、概览

Feign 客户端 中,可以使用 ErrorDecoder 或者 FallbackFactory 来处理异常。

2、Maven 依赖

创建一个 Spring Boot 项目,添加 spring-cloud-starter-openfeign 依赖,该 starter 已经包含了 feign-core 依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>3.1.3</version>
</dependency>

如果你想自定义要使用的 feign-core 的版本,你也可以手动在 pom.xml 文件中添加 feign-core 依赖:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-core</artifactId>
    <version>11.9.1</version>
</dependency>

3、使用 ErrorDecoder 处理异常

通过自定义 ErrorDecoder 来处理异常,可以在出现异常时返回自定义的异常。

如下:

public class RetreiveMessageErrorDecoder implements ErrorDecoder {
    private final 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);
        }
    }
}

在上述 encoder 中,我们覆盖了默认行为,对不同的异常状态返回了不同的异常。

4、使用 Fallback 处理异常

还可以通过配置 fallback 来处理异常。

先创建一个 FeignClient,然后配置 fallback

@FeignClient(name = "file", url = "http://localhost:8081", 
  configuration = FeignSupportConfig.class, fallback = FileUploadClientWithFallbackImpl.class)
public interface FileUploadClientWithFallBack {
    @PostMapping(value = "/upload-error", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String fileUpload(@RequestPart(value = "file") MultipartFile file);
}

现在,创建 FileUploadClientWithFallbackImpl,处理不同异常:

@Component
public class FileUploadClientWithFallbackImpl implements FileUploadClientWithFallBack {
    @Override
    public String fileUpload(MultipartFile file) {
        try {
            throw new NotFoundException("hi, something wrong");
        } catch (Exception ex) {
            if (ex instanceof BadRequestException) {
                return "Bad Request!!!";
            }
            if (ex instanceof NotFoundException) {
                return "Not Found!!!";
            }
            if (ex instanceof Exception) {
                return "Exception!!!";
            }
            return "Successfully Uploaded file!!!";
        }
    }
}

接着,创建一个简单的测试来验证 fallback 是否生效:

@Test(expected = NotFoundException.class)
public void whenFileUploadClientFallback_thenFileUploadError() throws IOException {
    ClassLoader classloader = Thread.currentThread().getContextClassLoader();
    File file = new File(classloader.getResource(FILE_NAME).getFile());
    Assert.assertTrue(file.exists());
    FileInputStream input = new FileInputStream(file);
    MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain",
      IOUtils.toByteArray(input));
    uploadService.uploadFileWithFallback(multipartFile);
}

5、使用 FallbackFactory 处理异常

还可以通过配置 FallbackFactory 来处理异常。

先创建一个 @FeignClient,然后配置 FallbackFactory

@FeignClient(name = "file", url = "http://localhost:8081", 
  configuration = FeignSupportConfig.class, fallbackFactory = FileUploadClientFallbackFactory.class)
public interface FileUploadClient {
    @PostMapping(value = "/upload-file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String fileUpload(@RequestPart(value = "file") MultipartFile file);
}

现在,创建 FileUploadClientFallbackFactory,处理不同异常:

@Component
public class FileUploadClientFallbackFactory implements FallbackFactory<FileUploadClient> {
    @Override
    public FileUploadClient create(Throwable cause) {
        return new FileUploadClient() {
            @Override
            public String fileUpload(MultipartFile file) {
                if (cause instanceof BadRequestException) {
                    return "Bad Request!!!";
                }
                if (cause instanceof NotFoundException) {
                    return "Not Found!!!";
                }
                if (cause instanceof Exception) {
                    return "Exception!!!";
                }
                return "Successfully Uploaded file!!!";
            }
        };
    }
}

创建测试,验证 FallbackFactory 是否生效:

@Test(expected = NotFoundException.class)
public void whenFileUploadClientFallbackFactory_thenFileUploadError() throws IOException {
    ClassLoader classloader = Thread.currentThread().getContextClassLoader();
    File file = new File(classloader.getResource(FILE_NAME).getFile());
    Assert.assertTrue(file.exists());
    FileInputStream input = new FileInputStream(file);
    MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain",
      IOUtils.toByteArray(input));
    uploadService.uploadFileWithFallbackFactory(multipartFile);
} 

6、总结

在使用 Feign Client 的时候,你可以通过 ErrorDecoder 或者自定义 fallbackfallbackFactory 实现来处理异常。


参考:https://www.baeldung.com/java-feign-client-exception-handling