FeignClient is pretty nifty tool, but unfortunately if you need to do something even a little different than Gods of Spring intended, you are in world of hurt.
Current case: I work with external third-party REST service that has, um, interesting concepts about REST. In particular, if you send to them correct JSON, they return HTTP 302 and JSON in body.
Needless to say, FeignClient really, really does not like this kind of shenanigans. My interface:
@FeignClient(value = "someClient", configuration = SomeClientDecoderConfiguration.class)
public interface SomeClient {
@RequestMapping(method = RequestMethod.POST, path = "${someClient.path}",
produces = MediaType.APPLICATION_JSON_VALUE)
JsonOrderCreateResponse orderCreate(JsonOrderCreateRequest request, @RequestHeader("Authorization") String authHeader);
}
I already have custom configuration and error decoder, but these work only on HTTP 4xx and 5xx. If system encounters 302, it ends up like that:
http://pastebin.com/raw/cGKWc4yg
How I can prevent that and force FeignClient to handle 302 like 200?
While there is no way to force FeignClient to treat normally anything beyond 2xx without crazy workarounds, there IS way to handle it with minimum hassle. Note that you will need very recent release! I had to enter this in POM:
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.9</version>
</dependency>
So, how to do it?
In feign client implementation for any method you need to return Response. That's it.
If you return Response, framework will behave in special way - in case of status over 2xx it won't throw any exceptions nor will try to retry many times over like some... gifted... child. In other words, no shenanigans. Yay!
Example:
import feign.Response;
@RequestMapping(method = RequestMethod.POST, path = "${someClient.path}")
Response doThisOrThat(JsonSomeRequest someJson, @RequestHeader("Authorization") String authHeader);
Of course, you will need to actually handle response by hand (beforementioned minimum hassle):
private JsonSomeResponse resolveResponse(JsonSomeRequest jsonRequest, String accessToken)
{
Response response = someClient.doThisOrThat(jsonRequest, "Bearer " + accessToken);
JsonSomeResponse jsonResponse = null;
String resultBody = "";
try {
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(response.body().asInputStream()))) {
resultBody = buffer.lines().collect(Collectors.joining("\n"));
}
jsonResponse = objectMapper.readValue(resultBody, JsonSomeResponse.class);
} catch (IOException ex) {
throw new RuntimeException("Failed to process response body.", ex);
}
// just an example of using exception
if (response.status() >= 400) thrown new OmgWeAllAreDeadException();
return jsonResponse;
}
Then you can do whatever you want, check response.status() to throw your own exceptions, treat response with any status code (like 302) in same way as 200 (oh the horror!) etc.