26

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 don't 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.

aSemy
  • 5,485
  • 2
  • 25
  • 51
scoder
  • 2,451
  • 4
  • 30
  • 70

6 Answers6

25

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.

aSemy
  • 5,485
  • 2
  • 25
  • 51
VaibS
  • 1,627
  • 1
  • 15
  • 21
  • 3
    you can remove `@Configuration` on `MyFeignClientConfiguration` as the class is instanciated via `configuration = MyFeignClientConfiguration.class`. – Stanislas Klukowski May 27 '21 at 12:41
15

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

aSemy
  • 5,485
  • 2
  • 25
  • 51
Srinath
  • 167
  • 1
  • 3
  • 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! – nelsw Oct 27 '20 at 16:18
  • 6
    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
  • As mentioned from the comment above, there is missing information that needs to be provided. Additionally, feign clients have mostly nothing to do with transport layers. – Omar El Hussein Jul 15 '22 at 14:14
6

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)
            }
        };
    }
   }
ikhvjs
  • 5,316
  • 2
  • 13
  • 36
rphlmr
  • 838
  • 6
  • 12
  • 3
    exception, witch has been thrown in fallback factory, will not catch by controller advice – KnockKnock Nov 26 '19 at 10:46
  • 2
    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
4

I'm kinda late too, but I wanted to make sure you check out other potential solutions as well.

Feign with Spring has a notoriously bad way of handling exceptions so I came up with a custom solution that creates this robust environment to define your business exceptions the way you want.

It's utilizing a custom ErrorDecoder registered to the Feign clients and adds the possibility to customize your exception handling based on method or class level.

Check it out: Maintainable error handling with Feign clients? Not a dream anymore

Arnold Galovics
  • 3,246
  • 3
  • 22
  • 33
  • I really appreciate your answer, but if you can post answer here instead of a link, that would be perfect! – ikhvjs Dec 16 '22 at 08:26
0

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
  • 533
  • 6
  • 22
kpankov
  • 11
  • 1
    That URL just leads to general Feign client documentation, not including Exception handling. – nelsw Oct 27 '20 at 16:17
0

String errorJson = e.contentUTF8();

HttpStatus status = HttpStatus.valueOf(e.status());

Map<String, List> errorResponse = objectMapper.readValue(errorJson, Map.class);

new ResponseDTO(status,e.status(), errorResponse.get("errors").get(0));