Netflix Feign - Propagate Status and Exception through Microservices

Shameless plug for a little library I did that uses reflection to dynamically rethrow checked exceptions (and unchecked if they are on the Feign interface) based on an error code returned in the body of the response.

More information on the readme : https://github.com/coveo/feign-error-decoder


OpenFeign's FeignException doesn't bind to a specific HTTP status (i.e. doesn't use Spring's @ResponseStatus annotation), which makes Spring default to 500 whenever faced with a FeignException. That's okay because a FeignException can have numerous causes that can't be related to a particular HTTP status.

However you can change the way that Spring handles FeignExceptions. Simply define an ExceptionHandler that handles the FeignException the way you need it (see here):

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(FeignException.class)
    public String handleFeignStatusException(FeignException e, HttpServletResponse response) {
        response.setStatus(e.status());
        return "feignError";
    }

}

This example makes Spring return the same HTTP status that you received from Microservice B. You can go further and also return the original response body:

response.getOutputStream().write(e.content());

You could use a feign ErrorDecoder

https://github.com/OpenFeign/feign/wiki/Custom-error-handling

Here is an example

public class MyErrorDecoder implements ErrorDecoder {

    private final ErrorDecoder defaultErrorDecoder = new Default();

    @Override
    public Exception decode(String methodKey, Response response) {
        if (response.status() >= 400 && response.status() <= 499) {
            return new MyBadRequestException();
        }
        return defaultErrorDecoder.decode(methodKey, response);
    }

}

For spring to pick up the ErrorDecoder you have to put it on the ApplicationContext:

@Bean
public MyErrorDecoder myErrorDecoder() {
  return new MyErrorDecoder();
}

Write your custom exception mapper and register it. You can customize responses.

Complete example is here

public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

    @Override
    public Response toResponse(Throwable ex) {
        return Response.status(500).entity(YOUR_RETURN_OBJ_HERE).build();
    }

}