diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index 596faa94..a015d6ab 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -175,6 +175,10 @@ feign: loggerLevel: full errorDecoder: com.example.SimpleErrorDecoder retryer: com.example.SimpleRetryer + defaultQueryParameters: + query: queryValue + defaultRequestHeaders: + header: headerValue requestInterceptors: - com.example.FooRequestInterceptor - com.example.BarRequestInterceptor @@ -188,6 +192,8 @@ Default configurations can be specified in the `@EnableFeignClients` attribute ` If you prefer using configuration properties to configured all `@FeignClient`, you can create configuration properties with `default` feign name. +You can use `feign.client.config.feignName.defaultQueryParameters` and `feign.client.config.feignName.defaultRequestHeaders` to specify query parameters and headers that will be sent with every request of the client named `feignName`. + application.yml [source,yaml] ---- diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java index ee5b3272..1bb38cdf 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java @@ -57,6 +57,7 @@ import org.springframework.util.StringUtils; * @author Gregor Zurowski * @author Matt King * @author Olga Maciaszek-Sharma + * @author Ilia Ilinykh */ class FeignClientFactoryBean implements FactoryBean, InitializingBean, ApplicationContextAware { @@ -244,6 +245,16 @@ class FeignClientFactoryBean builder.encoder(getOrInstantiate(config.getEncoder())); } + if (Objects.nonNull(config.getDefaultRequestHeaders())) { + builder.requestInterceptor(requestTemplate -> requestTemplate + .headers(config.getDefaultRequestHeaders())); + } + + if (Objects.nonNull(config.getDefaultQueryParameters())) { + builder.requestInterceptor(requestTemplate -> requestTemplate + .queries(config.getDefaultQueryParameters())); + } + if (Objects.nonNull(config.getDecoder())) { builder.decoder(getOrInstantiate(config.getDecoder())); } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java index 572ca528..c656c4a3 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java @@ -16,6 +16,7 @@ package org.springframework.cloud.openfeign; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,6 +35,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author Eko Kurniawan Khannedy + * @author Ilia Ilinykh */ @ConfigurationProperties("feign.client") public class FeignClientProperties { @@ -104,6 +106,10 @@ public class FeignClientProperties { private List> requestInterceptors; + private Map> defaultRequestHeaders; + + private Map> defaultQueryParameters; + private Boolean decode404; private Class decoder; @@ -163,6 +169,24 @@ public class FeignClientProperties { this.requestInterceptors = requestInterceptors; } + public Map> getDefaultRequestHeaders() { + return defaultRequestHeaders; + } + + public void setDefaultRequestHeaders( + Map> defaultRequestHeaders) { + this.defaultRequestHeaders = defaultRequestHeaders; + } + + public Map> getDefaultQueryParameters() { + return defaultQueryParameters; + } + + public void setDefaultQueryParameters( + Map> defaultQueryParameters) { + this.defaultQueryParameters = defaultQueryParameters; + } + public Boolean getDecode404() { return this.decode404; } @@ -224,7 +248,11 @@ public class FeignClientProperties { && Objects.equals(this.decoder, that.decoder) && Objects.equals(this.contract, that.contract) && Objects.equals(this.exceptionPropagationPolicy, - that.exceptionPropagationPolicy); + that.exceptionPropagationPolicy) + && Objects.equals(this.defaultRequestHeaders, + that.defaultRequestHeaders) + && Objects.equals(this.defaultQueryParameters, + that.defaultQueryParameters); } @Override @@ -232,7 +260,8 @@ public class FeignClientProperties { return Objects.hash(this.loggerLevel, this.connectTimeout, this.readTimeout, this.retryer, this.errorDecoder, this.requestInterceptors, this.decode404, this.encoder, this.decoder, this.contract, - this.exceptionPropagationPolicy); + this.exceptionPropagationPolicy, this.defaultQueryParameters, + this.defaultRequestHeaders); } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java index b97e2f54..095c8726 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java @@ -22,9 +22,15 @@ import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.net.SocketTimeoutException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; @@ -55,8 +61,11 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; @@ -66,6 +75,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen /** * @author Eko Kurniawan Khannedy * @author Olga Maciaszek-Sharma + * @author Ilia Ilinykh */ @SuppressWarnings("FieldMayBeFinal") @RunWith(SpringJUnit4ClassRunner.class) @@ -92,6 +102,10 @@ public class FeignClientUsingPropertiesTests { private FeignClientFactoryBean formFactoryBean; + private FeignClientFactoryBean defaultHeadersAndQuerySingleParamsFeignClientFactoryBean; + + private FeignClientFactoryBean defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean; + public FeignClientUsingPropertiesTests() { fooFactoryBean = new FeignClientFactoryBean(); fooFactoryBean.setContextId("foo"); @@ -108,6 +122,18 @@ public class FeignClientUsingPropertiesTests { formFactoryBean = new FeignClientFactoryBean(); formFactoryBean.setContextId("form"); formFactoryBean.setType(FeignClientFactoryBean.class); + + this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean = new FeignClientFactoryBean(); + this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean + .setContextId("singleValue"); + this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean + .setType(FeignClientFactoryBean.class); + + this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean = new FeignClientFactoryBean(); + this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean + .setContextId("multipleValue"); + this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean + .setType(FeignClientFactoryBean.class); } public FooClient fooClient() { @@ -159,6 +185,35 @@ public class FeignClientUsingPropertiesTests { assertThat(response).isEqualTo("Data"); } + @Test + public void testSingleValue() { + List response = singleValueClient().singleValue(); + assertThat(response).isEqualTo(Arrays.asList("header", "parameter")); + } + + @Test + public void testMultipleValue() { + List response = multipleValueClient().multipleValue(); + assertThat(response).isEqualTo( + Arrays.asList("header1", "header2", "parameter1", "parameter2")); + } + + public SingleValueClient singleValueClient() { + this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean + .setApplicationContext(this.applicationContext); + return this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean + .feign(this.context) + .target(SingleValueClient.class, "http://localhost:" + this.port); + } + + public MultipleValueClient multipleValueClient() { + this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean + .setApplicationContext(this.applicationContext); + return this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean + .feign(this.context) + .target(MultipleValueClient.class, "http://localhost:" + this.port); + } + @Test public void readTimeoutShouldWorkWhenConnectTimeoutNotSet() { FeignClientFactoryBean readTimeoutFactoryBean = new FeignClientFactoryBean(); @@ -202,21 +257,21 @@ public class FeignClientUsingPropertiesTests { protected interface FooClient { - @RequestMapping(method = RequestMethod.GET, value = "/foo") + @GetMapping(path = "/foo") String foo(); } protected interface BarClient { - @RequestMapping(method = RequestMethod.GET, value = "/bar") + @GetMapping(path = "/bar") String bar(); } protected interface UnwrapClient { - @RequestMapping(method = RequestMethod.GET, value = "/bar") // intentionally /bar + @GetMapping(path = "/bar") // intentionally /bar String unwrap() throws IOException; } @@ -229,6 +284,20 @@ public class FeignClientUsingPropertiesTests { } + protected interface SingleValueClient { + + @GetMapping(path = "/singleValue") + List singleValue(); + + } + + protected interface MultipleValueClient { + + @GetMapping(path = "/multipleValue") + List multipleValue(); + + } + protected interface TimeoutClient { @GetMapping("/timeouts") @@ -253,18 +322,33 @@ public class FeignClientUsingPropertiesTests { } } - @RequestMapping(method = RequestMethod.GET, value = "/bar") + @GetMapping(path = "/bar") public String bar() throws InterruptedException { - Thread.sleep(2000L); + TimeUnit.SECONDS.sleep(2); return "OK"; } - @RequestMapping(value = "/form", method = RequestMethod.POST, + @PostMapping(path = "/form", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public String form(HttpServletRequest request) { return request.getParameter("form"); } + @GetMapping(path = "/singleValue") + public List singleValue(@RequestHeader List singleValueHeaders, + @RequestParam List singleValueParameters) { + return Stream.of(singleValueHeaders, singleValueParameters) + .flatMap(Collection::stream).collect(Collectors.toList()); + } + + @GetMapping(path = "/multipleValue") + public List multipleValue( + @RequestHeader List multipleValueHeaders, + @RequestParam List multipleValueParameters) { + return Stream.of(multipleValueHeaders, multipleValueParameters) + .flatMap(Collection::stream).collect(Collectors.toList()); + } + } public static class FooRequestInterceptor implements RequestInterceptor { diff --git a/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties b/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties index e3248104..02a49414 100644 --- a/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties +++ b/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties @@ -10,6 +10,10 @@ feign.client.config.default.retryer=org.springframework.cloud.openfeign.FeignCli feign.client.config.default.decode404=true feign.client.config.foo.requestInterceptors[0]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FooRequestInterceptor feign.client.config.foo.requestInterceptors[1]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.BarRequestInterceptor +feign.client.config.singleValue.defaultRequestHeaders[singleValueHeaders]=header +feign.client.config.singleValue.defaultQueryParameters[singleValueParameters]=parameter +feign.client.config.multipleValue.defaultRequestHeaders[multipleValueHeaders]=header1,header2 +feign.client.config.multipleValue.defaultQueryParameters[multipleValueParameters]=parameter1,parameter2 feign.client.config.bar.connectTimeout=1000 feign.client.config.bar.readTimeout=1000 feign.client.config.form.encoder=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FormEncoder