From 0705c9f540132ea9da717d0bb4f9754f8678b834 Mon Sep 17 00:00:00 2001 From: Damian Jackson Date: Sat, 28 May 2016 14:45:04 +0100 Subject: [PATCH] Support header placeholders in SpringMvcContract. When using property placeholders in the @RequestMapping annotation the ones placed in the headers attribute do not get replaced. This appears to be because the other attributes, such as the url, use the resolve() function to perform the substitution whereas the header is parsed as-is. This change adds a call to resolve for each side of the equals in the header string so that the properties are substituted correctly. --- .../feign/support/SpringMvcContract.java | 6 +- .../netflix/feign/valid/FeignClientTests.java | 85 +++++++++++-------- .../src/test/resources/application.yml | 3 +- 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java index 0e8a2824..97723a5f 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java @@ -266,8 +266,8 @@ public class SpringMvcContract extends Contract.BaseContract if (annotation.headers() != null && annotation.headers().length > 0) { for (String header : annotation.headers()) { int index = header.indexOf('='); - md.template().header(header.substring(0, index), - header.substring(index + 1).trim()); + md.template().header(resolve(header.substring(0, index)), + resolve(header.substring(index + 1).trim())); } } } @@ -358,4 +358,4 @@ public class SpringMvcContract extends Contract.BaseContract } } -} \ No newline at end of file +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java index 5c7e2e6c..f9a44600 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java @@ -137,51 +137,54 @@ public class FeignClientTests { } } - @FeignClient(value = "localapp", configuration = TestClientConfig.class) + @FeignClient(name = "localapp", configuration = TestClientConfig.class) protected interface TestClient { - @RequestMapping(method = RequestMethod.GET, value = "/hello") + @RequestMapping(method = RequestMethod.GET, path = "/hello") Hello getHello(); - @RequestMapping(method = RequestMethod.GET, value = "${feignClient.methodLevelRequestMappingPath}") + @RequestMapping(method = RequestMethod.GET, path = "${feignClient.methodLevelRequestMappingPath}") Hello getHelloUsingPropertyPlaceHolder(); - @RequestMapping(method = RequestMethod.GET, value = "/hello") + @RequestMapping(method = RequestMethod.GET, path = "/hello") Single getHelloSingle(); - @RequestMapping(method = RequestMethod.GET, value = "/hellos") + @RequestMapping(method = RequestMethod.GET, path = "/hellos") List getHellos(); - @RequestMapping(method = RequestMethod.GET, value = "/hellostrings") + @RequestMapping(method = RequestMethod.GET, path = "/hellostrings") List getHelloStrings(); - @RequestMapping(method = RequestMethod.GET, value = "/helloheaders") + @RequestMapping(method = RequestMethod.GET, path = "/helloheaders") List getHelloHeaders(); - @RequestMapping(method = RequestMethod.GET, value = "/helloparams") + @RequestMapping(method = RequestMethod.GET, path = "/helloheadersplaceholders", headers = "myPlaceholderHeader=${feignClient.myPlaceholderHeader}") + String getHelloHeadersPlaceholders(); + + @RequestMapping(method = RequestMethod.GET, path = "/helloparams") List getParams(@RequestParam("params") List params); - @RequestMapping(method = RequestMethod.GET, value = "/hellos") + @RequestMapping(method = RequestMethod.GET, path = "/hellos") HystrixCommand> getHellosHystrix(); - @RequestMapping(method = RequestMethod.GET, value = "/noContent") + @RequestMapping(method = RequestMethod.GET, path = "/noContent") ResponseEntity noContent(); - @RequestMapping(method = RequestMethod.HEAD, value = "/head") + @RequestMapping(method = RequestMethod.HEAD, path = "/head") ResponseEntity head(); - @RequestMapping(method = RequestMethod.GET, value = "/hello") + @RequestMapping(method = RequestMethod.GET, path = "/hello") HttpEntity getHelloEntity(); @RequestMapping(method = RequestMethod.POST, consumes = "application/vnd.io.spring.cloud.test.v1+json", produces = "application/vnd.io.spring.cloud.test.v1+json", - value = "/complex") + path = "/complex") String moreComplexContentType(String body); - @RequestMapping(method = RequestMethod.GET, value = "/tostring") + @RequestMapping(method = RequestMethod.GET, path = "/tostring") String getToString(@RequestParam("arg") Arg arg); - @RequestMapping(method = RequestMethod.GET, value = "/tostring2") + @RequestMapping(method = RequestMethod.GET, path = "/tostring2") String getToString(@RequestParam("arg") OtherArg arg); } @@ -210,31 +213,31 @@ public class FeignClientTests { @FeignClient(name = "localapp1") protected interface TestClientServiceId { - @RequestMapping(method = RequestMethod.GET, value = "/hello") + @RequestMapping(method = RequestMethod.GET, path = "/hello") Hello getHello(); } @FeignClient(name = "localapp2", decode404 = true) protected interface DecodingTestClient { - @RequestMapping(method = RequestMethod.GET, value = "/notFound") + @RequestMapping(method = RequestMethod.GET, path = "/notFound") ResponseEntity notFound(); } @FeignClient(name = "localapp3", fallback = HystrixClientFallback.class) protected interface HystrixClient { - @RequestMapping(method = RequestMethod.GET, value = "/fail") + @RequestMapping(method = RequestMethod.GET, path = "/fail") Single failSingle(); - @RequestMapping(method = RequestMethod.GET, value = "/fail") + @RequestMapping(method = RequestMethod.GET, path = "/fail") Hello fail(); - @RequestMapping(method = RequestMethod.GET, value = "/fail") + @RequestMapping(method = RequestMethod.GET, path = "/fail") HystrixCommand failCommand(); - @RequestMapping(method = RequestMethod.GET, value = "/fail") + @RequestMapping(method = RequestMethod.GET, path = "/fail") Observable failObservable(); - @RequestMapping(method = RequestMethod.GET, value = "/fail") + @RequestMapping(method = RequestMethod.GET, path = "/fail") Future failFuture(); } @@ -307,23 +310,23 @@ public class FeignClientTests { }; } - @RequestMapping(method = RequestMethod.GET, value = "/hello") + @RequestMapping(method = RequestMethod.GET, path = "/hello") public Hello getHello() { return new Hello(HELLO_WORLD_1); } - @RequestMapping(method = RequestMethod.GET, value = "/hello2") + @RequestMapping(method = RequestMethod.GET, path = "/hello2") public Hello getHello2() { return new Hello(OI_TERRA_2); } - @RequestMapping(method = RequestMethod.GET, value = "/hellos") + @RequestMapping(method = RequestMethod.GET, path = "/hellos") public List getHellos() { ArrayList hellos = getHelloList(); return hellos; } - @RequestMapping(method = RequestMethod.GET, value = "/hellostrings") + @RequestMapping(method = RequestMethod.GET, path = "/hellostrings") public List getHelloStrings() { ArrayList hellos = new ArrayList<>(); hellos.add(HELLO_WORLD_1); @@ -331,7 +334,7 @@ public class FeignClientTests { return hellos; } - @RequestMapping(method = RequestMethod.GET, value = "/helloheaders") + @RequestMapping(method = RequestMethod.GET, path = "/helloheaders") public List getHelloHeaders(@RequestHeader(MYHEADER1) String myheader1, @RequestHeader(MYHEADER2) String myheader2) { ArrayList headers = new ArrayList<>(); @@ -340,27 +343,32 @@ public class FeignClientTests { return headers; } - @RequestMapping(method = RequestMethod.GET, value = "/helloparams") + @RequestMapping(method = RequestMethod.GET, path = "/helloheadersplaceholders") + public String getHelloHeadersPlaceholders(@RequestHeader("myPlaceholderHeader") String myPlaceholderHeader) { + return myPlaceholderHeader; + } + + @RequestMapping(method = RequestMethod.GET, path = "/helloparams") public List getParams(@RequestParam("params") List params) { return params; } - @RequestMapping(method = RequestMethod.GET, value = "/noContent") + @RequestMapping(method = RequestMethod.GET, path = "/noContent") ResponseEntity noContent() { return ResponseEntity.noContent().build(); } - @RequestMapping(method = RequestMethod.HEAD, value = "/head") + @RequestMapping(method = RequestMethod.HEAD, path = "/head") ResponseEntity head() { return ResponseEntity.ok().build(); } - @RequestMapping(method = RequestMethod.GET, value = "/fail") + @RequestMapping(method = RequestMethod.GET, path = "/fail") String fail() { throw new RuntimeException("always fails"); } - @RequestMapping(method = RequestMethod.GET, value = "/notFound") + @RequestMapping(method = RequestMethod.GET, path = "/notFound") ResponseEntity notFound() { return ResponseEntity.status(HttpStatus.NOT_FOUND).body((String)null); } @@ -368,17 +376,17 @@ public class FeignClientTests { @RequestMapping(method = RequestMethod.POST, consumes = "application/vnd.io.spring.cloud.test.v1+json", produces = "application/vnd.io.spring.cloud.test.v1+json", - value = "/complex") + path = "/complex") String complex(String body) { return "{\"value\":\"OK\"}"; } - @RequestMapping(method = RequestMethod.GET, value = "/tostring") + @RequestMapping(method = RequestMethod.GET, path = "/tostring") String getToString(@RequestParam("arg") Arg arg) { return arg.toString(); } - @RequestMapping(method = RequestMethod.GET, value = "/tostring2") + @RequestMapping(method = RequestMethod.GET, path = "/tostring2") String getToString(@RequestParam("arg") OtherArg arg) { return arg.value; } @@ -437,6 +445,13 @@ public class FeignClientTests { headers.contains("myheader2value")); } + @Test + public void testHeaderPlaceholders() { + String header = this.testClient.getHelloHeadersPlaceholders(); + assertNotNull("header was null", header); + assertEquals("header was wrong", "myPlaceholderHeaderValue", header); + } + @Test public void testFeignClientType() throws IllegalAccessException { assertThat(this.feignClient, is(instanceOf(LoadBalancerFeignClient.class))); diff --git a/spring-cloud-netflix-core/src/test/resources/application.yml b/spring-cloud-netflix-core/src/test/resources/application.yml index 4b1832d2..5d440473 100644 --- a/spring-cloud-netflix-core/src/test/resources/application.yml +++ b/spring-cloud-netflix-core/src/test/resources/application.yml @@ -47,4 +47,5 @@ zuul: path: /stores/** feignClient: localappName: localapp - methodLevelRequestMappingPath: /hello2 \ No newline at end of file + methodLevelRequestMappingPath: /hello2 + myPlaceholderHeader: myPlaceholderHeaderValue \ No newline at end of file