diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index fdbad182..291b0109 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -173,6 +173,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 @@ -186,6 +190,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 4fb5f88e..50d46d84 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 @@ -56,6 +56,7 @@ import org.springframework.util.StringUtils; * @author Gregor Zurowski * @author Matt King * @author Olga Maciaszek-Sharma + * @author Ilia Ilinykh */ public class FeignClientFactoryBean implements FactoryBean, InitializingBean, ApplicationContextAware { @@ -237,6 +238,14 @@ public class FeignClientFactoryBean implements FactoryBean, Initializing 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 072320be..0e8df314 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 { @@ -103,6 +105,10 @@ public class FeignClientProperties { private List> requestInterceptors; + private Map> defaultRequestHeaders; + + private Map> defaultQueryParameters; + private Boolean decode404; private Class decoder; @@ -161,6 +167,22 @@ 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; } @@ -216,14 +238,17 @@ public class FeignClientProperties { && Objects.equals(this.requestInterceptors, that.requestInterceptors) && Objects.equals(this.decode404, that.decode404) && Objects.equals(this.encoder, that.encoder) && Objects.equals(this.decoder, that.decoder) && Objects.equals(this.contract, that.contract) - && Objects.equals(this.exceptionPropagationPolicy, that.exceptionPropagationPolicy); + && Objects.equals(this.exceptionPropagationPolicy, that.exceptionPropagationPolicy) + && Objects.equals(this.defaultRequestHeaders, that.defaultRequestHeaders) + && Objects.equals(this.defaultQueryParameters, that.defaultQueryParameters); } @Override public int hashCode() { 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.contract, 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 9dc3b3bc..b9206515 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) @@ -91,6 +101,10 @@ public class FeignClientUsingPropertiesTests { private FeignClientFactoryBean formFactoryBean; + private FeignClientFactoryBean defaultHeadersAndQuerySingleParamsFeignClientFactoryBean; + + private FeignClientFactoryBean defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean; + public FeignClientUsingPropertiesTests() { fooFactoryBean = new FeignClientFactoryBean(); fooFactoryBean.setContextId("foo"); @@ -107,6 +121,14 @@ 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() { @@ -154,6 +176,30 @@ 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(); @@ -196,21 +242,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; } @@ -223,6 +269,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") @@ -246,18 +306,31 @@ 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, - consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @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