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
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 super T, ? extends U> 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 :/