Feign Client Error Handling - Suppress the Error/Exception and convert to 200 success response

6.2k views Asked by At

I am using feign client to connect to downstream service.

I got a requirement that when one of the downstream service endpoint returns 400 ( it's partial success scenario ) our service need this to be converted to 200 success with the response value.

I am looking for a best way of doing this.

We are using error decoder to handle the errors and the above conversion is applicable for only one endpoint not for all the downstream endpoints and noticed that decode() method should returns exception back.

3

There are 3 answers

2
Kevin Davis On BEST ANSWER

You will need to create a customized Client to intercept the Response early enough to change the response status and not invoke the ErrorDecoder. The simplest approach is to create a wrapper on an existing client and create a new Response with a 200 status. Here is an example when using Feign's ApacheHttpClient:

public class ClientWrapper extends ApacheHttpClient {
   private ApacheHttpClient delegate;

   public ClientWrapper(ApacheHttpClient client) {
      this.client = client;
   }

   @Override
   public Response execute(Request request, Request.Options options) throws IOException {
      /* execute the request on the delegate */
      Response response = this.client.execute(request, options);

      /* check the response code and change */
      if (response.status() == 400) {
         response = Response.builder(response).status(200).build();
      }
      return response;
   }
}

This customized client can be used on any Feign client you need.

1
Shanid On

Another way of doing is by throwing custom exception at error decoder and convert this custom exception to success at spring global exception handler (using @RestControllerAdvice )

public class CustomErrorDecoder implements ErrorDecoder {

@Override
public Exception decode(String methodKey, Response response) {

    if (response.status() == 400 && response.request().url().contains("/wanttocovert400to200/clientendpoints") {
        ResponseData responseData;
        ObjectMapper mapper = new ObjectMapper();
        try {
            responseData = mapper.readValue(response.body().asInputStream(), ResponseData.class);
        } catch (Exception e) {
            responseData = new ResponseData();
        }
        return new PartialSuccessException(responseData); 
    }
    return FeignException.errorStatus(methodKey, response);
}}

And the Exception handler as below

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(PartialSuccessException.class)
    public ResponseData handlePartialSuccessException(
            PartialSuccessException ex) {
        return ex.getResponseData();
    }
}
0
Wallace Jackson On

Change the microservice response:

public class CustomFeignClient extends Client.Default {

  public CustomFeignClient(
    final SSLSocketFactory sslContextFactory, final HostnameVerifier 
        hostnameVerifier) {
    super(sslContextFactory, hostnameVerifier);
  }

  @Override
  public Response execute(final Request request, final Request.Options 
    options) throws IOException {
    Response response = super.execute(request, options);
    if (HttpStatus.SC_OK != response.status()) {
      response =
        Response.builder()
          .status(HttpStatus.SC_OK)
          .body(InputStream.nullInputStream(), 0)
          .headers(response.headers())
          .request(response.request())
          .build();
    }
    return response;
  }
}

Add a Feign Client Config:

@Configuration
public class FeignClientConfig {
  @Bean
  public Client client() {
    return new CustomFeignClient(null, null);
  }
}