Describe the bug
When returning an empty org.springframework.core.io.Resource response (e.g. FileSystemResource pointing to an empty file) SpringDecoder converts it to a null instead of an empty Resource.
Version: org.springframework.cloud:spring-cloud-openfeign-core:2.1.3.RELEASE
Source of the problem
Line 59 in SpringDecoder uses HttpMessageConverterExtractor to extract the data from the HTTP response using the assigned MessageConverter. Unfortunately Line 89 in the HttpMessageConverterExtractor class has an early return when the body is empty. This is the case when returning an empty Resource.
Comment From: spencergibb
Why are you returning a Resource from feign?
Comment From: czocher
I'm returning a FileSystemResource from a controller because I need file-download functionality. I am aware Feign is not the best fit for this kind of functionality, but I'm constrained by the environment.
Comment From: czocher
For the people searching for a quick-fix: this issue can be bypassed by creating a new delegating Decoder implementation and decorating the SpringDecoder as follows:
public class EmptyResourceDecoder implements Decoder {
public static final ByteArrayResource EMPTY_RESPONSE = new ByteArrayResource("".getBytes(StandardCharsets.UTF_8));
private final Decoder delegate;
public EmptyResourceDecoder(final Decoder delegate) {
Objects.requireNonNull(delegate, "Decoder must not be null. ");
this.delegate = delegate;
}
@Override
public Object decode(final Response response, final Type type) throws IOException, FeignException {
final Object result = delegate.decode(response, type);
if (type instanceof Class && ((Class<?>) type).isAssignableFrom(Resource.class) && result == null) {
return EMPTY_RESPONSE;
}
return result;
}
}
@Bean
public Decoder feignDecoder(final ObjectFactory<HttpMessageConverters> messageConverters) {
return new OptionalDecoder(
new ResponseEntityDecoder(new EmptyResourceDecoder(new SpringDecoder(messageConverters))));
}
Comment From: spencergibb
Why can't the feign client just return a byte array?
Comment From: czocher
The return type must be the same as the one in the Type argument otherwise a ClassCastException occurs due to the fact the return of this method is later casted to the Resource type expected by the Feign client interface (java.lang.ClassCastException: [B cannot be cast to org.springframework.core.io.Resource to be exact).
EDIT: Ah you mean the Feign client interface? It is taken from the Controller interface which should support streaming responses. I believe Resource actually provides that, although not in the Feign Client case. Also it simplifies the implementation on the backend side.
Comment From: spencergibb
Yes, this is a problem when you share an interface between a client and server. I'm going decline this as we don't want to support everything you can do on a server on a client.