FeignClient throws instead of returning ResponseEntity with error http status

后端 未结 2 1941
谎友^
谎友^ 2020-12-18 11:35

As I\'m using ResponseEntity as return value for my FeignClient method, I was expecting it to return a ResponseEntity with 400 status if it\'s what the

2条回答
  •  既然无缘
    2020-12-18 12:06

    So, looking at source code, it seams that only solution is actually using feign.Response as return type for FeignClient methods and hand decoding the body with something like new ObjectMapper().readValue(response.body().asReader(), clazz) (with a guard on 2xx status of course because for error statuses, it's very likely that body is an error description and not a valid payload ;).

    This makes possible to extract and forward status, header, body, etc. even if status is not in 2xx range.

    Edit: Here is a way to forward status, headers and mapped JSON body (if possible):

    public static class JsonFeignResponseHelper {
        private final ObjectMapper json = new ObjectMapper();
    
        public  Optional decode(Response response, Class clazz) {
            if(response.status() >= 200 && response.status() < 300) {
                try {
                    return Optional.of(json.readValue(response.body().asReader(), clazz));
                } catch(IOException e) {
                    return Optional.empty();
                }
            } else {
                return Optional.empty();
            }
        }
    
        public  ResponseEntity toResponseEntity(Response response, Class clazz, Function mapper) {
            Optional payload = decode(response, clazz).map(mapper);
    
            return new ResponseEntity(
                    payload.orElse(null),//didn't find a way to feed body with original content if payload is empty
                    convertHeaders(response.headers()),
                    HttpStatus.valueOf(response.status()));
        }
    
        public MultiValueMap  convertHeaders(Map> responseHeaders) {
            MultiValueMap responseEntityHeaders = new LinkedMultiValueMap<>();
            responseHeaders.entrySet().stream().forEach(e -> 
                    responseEntityHeaders.put(e.getKey(), new ArrayList<>(e.getValue())));
            return responseEntityHeaders;
        }
    }
    

    that can be used as follow:

    @PostMapping("/login")
    public ResponseEntity getTokens(@RequestBody @Valid LoginRequest userCredentials) throws IOException {
        Response response = oauthFeignClient.token();
    
        return feignHelper.toResponseEntity(
                response,
                OauthTokenResponse.class,
                oauthTokenResponse -> new LoginTokenPair(
                        new BearerToken(oauthTokenResponse.access_token, oauthTokenResponse.expires_in),
                        new BearerToken(oauthTokenResponse.refresh_token, refreshTokenValidity)));
    }
    

    This saves headers and status code, but error message is lost :/

提交回复
热议问题