diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/SpringDecoder.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/SpringDecoder.java index 059e61ba..179da79b 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/SpringDecoder.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/SpringDecoder.java @@ -2,6 +2,7 @@ package org.springframework.cloud.netflix.feign; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.springframework.beans.factory.annotation.Autowired; @@ -31,18 +32,17 @@ public class SpringDecoder implements Decoder { @Override public Object decode(final Response response, Type type) throws IOException, FeignException { - if (type instanceof Class) { - @SuppressWarnings({ "unchecked", "rawtypes" }) - HttpMessageConverterExtractor extractor = new HttpMessageConverterExtractor( - (Class) type, messageConverters.getConverters()); + if (type instanceof Class || type instanceof ParameterizedType) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + HttpMessageConverterExtractor extractor = new HttpMessageConverterExtractor( + type, messageConverters.getConverters()); - Object data = extractor.extractData(new FeignResponseAdapter(response)); - return data; - } - throw new DecodeException("type is not an instance of Class: " + type); + return extractor.extractData(new FeignResponseAdapter(response)); + } + throw new DecodeException("type is not an instance of Class or ParameterizedType: " + type); } - private class FeignResponseAdapter implements ClientHttpResponse { + private class FeignResponseAdapter implements ClientHttpResponse { private final Response response; private FeignResponseAdapter(Response response) { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java new file mode 100644 index 00000000..2552e866 --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java @@ -0,0 +1,111 @@ +package org.springframework.cloud.netflix.feign; + +import static org.junit.Assert.*; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Spencer Gibb + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = SpringDecoderTests.Application.class) +@WebAppConfiguration +@IntegrationTest({ "server.port=0", "spring.application.name=springdecodertest", "spring.jmx.enabled=true" }) +public class SpringDecoderTests extends FeignConfigurer { + + @Value("${local.server.port}") + private int port = 0; + + public TestClient testClient() { + return feign().target(TestClient.class, "http://localhost:"+port); + } + + protected static interface TestClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") + public Hello getHello(); + + @RequestMapping(method = RequestMethod.GET, value = "/hellos") + public List getHellos(); + + @RequestMapping(method = RequestMethod.GET, value = "/hellostrings") + public List getHelloStrings(); + } + + @Configuration + @EnableAutoConfiguration + @RestController + protected static class Application implements TestClient { + + public Hello getHello() { + return new Hello("hello world 1"); + } + + public List getHellos() { + ArrayList hellos = new ArrayList<>(); + hellos.add(new Hello("hello world 1")); + hellos.add(new Hello("oi terra 2")); + return hellos; + } + + public List getHelloStrings() { + ArrayList hellos = new ArrayList<>(); + hellos.add("hello world 1"); + hellos.add("oi terra 2"); + return hellos; + } + + public static void main(String[] args) { + new SpringApplicationBuilder(Application.class).properties( + "spring.application.name=springdecodertest", "management.contextPath=/admin") + .run(args); + } + } + + @Test + public void testSimpleType() { + Hello hello = testClient().getHello(); + assertNotNull("hello was null", hello); + assertEquals("first hello didn't match", new Hello("hello world 1"), hello); + } + + @Test + public void testUserParameterizedTypeDecode() { + List hellos = testClient().getHellos(); + assertNotNull("hellos was null", hellos); + assertEquals("hellos was not the right size", 2, hellos.size()); + assertEquals("first hello didn't match", new Hello("hello world 1"), hellos.get(0)); + } + + @Test + public void testSimpleParameterizedTypeDecode() { + List hellos = testClient().getHelloStrings(); + assertNotNull("hellos was null", hellos); + assertEquals("hellos was not the right size", 2, hellos.size()); + assertEquals("first hello didn't match", "hello world 1", hellos.get(0)); + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Hello { + private String message; + } +}