Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we'll demonstrate how to handle exceptions in Feign. Feign is a powerful tool for micro-service developers, and it supports ErrorDecoder and FallbackFactory for exception handling.

2. Maven Dependency

To start, let's create a Spring Boot project by including the spring-cloud-starter-openfeign. The spring-cloud-starter-openfeign includes feign-core dependency within it:

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

Or we can add the feign-core dependency to our pom.xml file:

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

3. Exception Handling with ErrorDecoder

We can handle exceptions by configuring ErrorDecoder, which also allows us to customize messages when required. When an error occurs, the Feign client suppresses the original message. To retrieve it, we can write a custom ErrorDecoder.  Let's override the default ErrorDecoder implementation:

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);
        }
    }
}

In the above encoder, we override the default behavior to take more control of the exceptions.

4.  Exception Handling with Fallback

We can also handle exceptions by configuring fallback. Let's create a client first and configure 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);
}

Now, let's create FileUploadClientWithFallbackImpl to handle the exceptions according to our requirements:

@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!!!";
        }
    }
}

Let's now create a simple test to validate fallback option:

@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.  Exception Handling with FallbackFactory

We can also handle exceptions by configuring FallbackFactory. Let's create a client first and configure 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);
}

Now, let's create FileUploadClientFallbackFactory to handle the exceptions according to our requirements:

@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!!!";
            }
        };
    }
}

Let's now create a simple test to validate the FallbackFactory option:

@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. Conclusion

In this article, we've demonstrated how we can handle exceptions in feign.

As always, all code samples used in this tutorial are available over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Cloud footer banner
Comments are closed on this article!