I have a feign client service built like this :
Feign.Builder builder = Feign.builder()
.contract(new SpringMvcContract())
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.errorDecoder(new FeignClientErrorHandler())
return builder.target(targetClass, url);
I have the FeignClientErrorDecoder
which looks like this :
public class FeignClientErrorDecoder implements ErrorDecoder {
private static final ObjectMapper MAPPER = new ObjectMapper().registerModule(new JavaTimeModule());
@Override
public Exception decode(final String methodKey,
final Response response) {
try {
byte[] body = Util.toByteArray(response.body().asInputStream());
ApiError apiError = MAPPER.readValue(body, ApiError.class);
return ExceptionFactory.createFrom(apiError);
} catch (IOException | ApiErrorException e) {
return new TechnicalClientException("Could not extract error payload.", e);
}
}
}
No matter which reading input stream solution I choose, I always get a stream is closed
error.
What am I missing ? Who is closing it ? Any workaround ?
Complete code here :
https://github.com/louisamoros/feign-error-code
You can run mvn clean install
and see that 1 test is in error.
Thanks for the full code. The error occurs due to logging of response
in error decoder:
LOGGER.error("Feign client error handler. Method: {}, Response: {}", methodKey, response);
Here, toString()
is called on the response, including its body. So, input stream of the response body is read and closed there and cannot be read again later.
You can either remove response
from logging or copy its input stream (via apache IOUtils or smth similar) and then work with its duplicate. In that case, the mapper will parse everything successfully and the next line return new ApiException()
will be reached.
By the way, be careful with debugging this kind of code. Modern IDEs (e.g. IntelliJ IDEA) can invoke toString()
on all objects in the scope while reaching a breakpoint by default, so the same problem can occur because of it too. Here, you can safely put the breakpoint only after MAPPER.readValue
line.