9

I have some fiegn client to send request other micro service.

@FeignClient(name="userservice")
public interface UserClient {

    @RequestMapping(
            method= RequestMethod.GET,
                      path = "/userlist")
    String getUserByid(@RequestParam(value ="id") String id);

}

Now I am sending request like this

try {
    String responseData = userClient.getUserByid(id);
    return responseData;
    }

catch(FeignException e)
 {
 logger.error("Failed to get user", id);
}

catch (Exception e) 
{
 logger.error("Failed to get user", id);
}

Here the problem is if any FeignException happens I dont get any error code.

I need to send a corresponding error codes in other APIS to send to caller

So how to extract the error code? I want to extract error code and build a responseEntity

I got this code but dont know how exactly I can use in my function.

kcoder
  • 1,701
  • 2
  • 18
  • 39

4 Answers4

8

Not the same issue, but this helped in my situation. 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

@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

radrow
  • 4,248
  • 1
  • 19
  • 36
Srinath
  • 89
  • 1
  • 2
  • how are you configuring this? its not working for me. Do i need to add additional code for config to scoop this up> – Stevers Feb 21 '20 at 19:52
  • How can I return the response body of FeignException for http status other than 2xx? – Juan Rojas Jul 02 '20 at 23:08
  • This is the only answer that worked for me, thank you! – ConnorVanElswyk Oct 27 '20 at 16:18
  • 1
    Even though this works it promote a bad practice, which is leaking a low level implementation detail up to the stack. The FeignClient is used within the application layer to provide some value to the domain, it has nothing to do with the transport layer (i.e. controllers, http statutes etc). If you want to go this way I would recommend first to wrap the `FeignException` into a proper domain one, e.g. `UserNotFoundException` and only then using the method you proposed on the latter. – th3n3rd May 14 '21 at 07:57
3

did you try to implement FallbackFactory on your feign client ?

https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#spring-cloud-feign-hystrix-fallback

On the create method, before return, you can retrieve the http status code with this snippet :

String httpStatus = cause instanceof FeignException ? Integer.toString(((FeignException) cause).status()) : "";

Exemple :

@FeignClient(name="userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {

    @RequestMapping(
            method= RequestMethod.GET,
                      path = "/userlist")
    String getUserByid(@RequestParam(value ="id") String id);

}


@Component
static class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable cause) {

     String httpStatus = cause instanceof FeignException ? Integer.toString(((FeignException) cause).status()) : "";

     return new UserClient() {
        @Override
        public String getUserByid() {
            logger.error(httpStatus);
            // what you want to answer back (logger, exception catch by a ControllerAdvice, etc)
        }
    };
}

}

rphlmr
  • 406
  • 2
  • 9
  • 2
    exception, witch has been thrown in fallback factory, will not catch by controller advice – KnockKnock Nov 26 '19 at 10:46
  • In a general case, there will be multiple methods in controller, so do we go ahead by implementing each and every method even if all I want to do handle it for few methods not all – Akki Apr 16 '20 at 04:47
1

I'm late to party but here are my 2 cents. We had same use case to handle exceptions based on error code and we used custom ErrorDecoder.

public class CustomErrorDecoder implements ErrorDecoder {

    @Override
    public Exception decode(String methodKey, Response response) {
        String requestUrl = response.request().url();
        Response.Body responseBody = response.body();
        HttpStatus responseStatus = HttpStatus.valueOf(response.status());

        if (responseStatus.is5xxServerError()) {
            return new RestApiServerException(requestUrl, responseBody);
        } else if (responseStatus.is4xxClientError()) {
            return new RestApiClientException(requestUrl, responseBody);
        } else {
            return new Exception("Generic exception");
        }
    }
}

Return @Bean of above class in FeignClientConfiguration class.

public class MyFeignClientConfiguration {

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

Use this as your config class for FeignClient.

@FeignClient(value = "myFeignClient", configuration = MyFeignClientConfiguration.class)

Then you can handle these exceptions using GlobalExceptionHandler.

VaibS
  • 755
  • 1
  • 7
  • 15
  • you can remove `@Configuration` on `MyFeignClientConfiguration` as the class is instanciated via `configuration = MyFeignClientConfiguration.class`. – Stanislas Klukowski May 27 '21 at 12:41
-1

There's a ErrorDecored interface for that like it says in the documentation

The answer above about FallbackFactory is viable but falls into some other layer of abstraction.

leaqui
  • 473
  • 5
  • 18