Java Automation Job Top
We’re looking for a Backend Java/Spring Developer with Integration Experience: Read More

1. Overview

A RESTful service can fail for any number of reasons. In this tutorial, we'll look at how to retrieve the original message from the Feign client if the integrated REST service throws an error.

2. Feign Client

Feign is a pluggable and declarative web service client that makes writing web service clients easier. In addition, to Feign annotations, it also supports JAX-RS, and it supports encoders and decoders to provide more customization.

3. Retrieving Message From ErrorDecoder

When errors occur, the Feign client suppresses the original message, and to retrieve it, we require to write a custom ErrorDecoder. In the absence of such customization, we'll get the following error:

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]

To handle this error, we'll create a simple ExceptionMessage Java bean representing the error message:

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

Let's retrieve the original message by extracting it in our customized implementation of 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);
        }
    }
}

In our implementation, we've added the logic based on possible errors, and hence we can customize them to meet our requirements. In our switch block's default case, we're using Default implementation of ErrorDecoder.

Default implementation decodes the HTTP response when the status is not in the 2xx range. When throwable is retryable, it should be a subtype of RetryableException, and we should raise application-specific exceptions whenever possible.

To configure our customized ErrorDecoder, we'll add our implementation as a bean in the Feign configuration:

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

Now, let's see the exception with the original message:

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

In this article, we've demonstrated how to customize ErrorDecoder so we can catch Feign errors to fetch the original message.

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

REST bottom

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

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