Compare commits

...
Author SHA1 Message Date
buildmaster 6f09ae0824 Update SNAPSHOT to 2.2.2.RELEASE 2020-03-04 16:28:48 +00:00
Olga Maciaszek-Sharma c82b577d00 Upgrade feign to 10.7.4. (#292) 2020-02-05 14:37:35 +01:00
Ryan Baxter eca40256d8 Merge remote-tracking branch 'origin/2.1.x' into 2.2.x 2020-01-29 11:00:20 -05:00
Ryan Baxter a85294a835 Add documentation about early initialization errors. Fixes #221 2020-01-29 10:57:58 -05:00
Ryan Baxter db778b5c74 Merge remote-tracking branch 'origin/2.1.x' into 2.2.x 2020-01-29 10:38:31 -05:00
Ryan Baxter 73a75fd8b2 Add appendix.adoc. Fixes #255 2020-01-29 10:38:13 -05:00
Aaron Whiteside 892e65e303 Fix GH-201: Multiple @RequestPart not Working (#258)
Summary of changes follows:

- Delegate ALL requestBody encoding where Content-Type is multipart/form-data to the SpringFormEncoder.
- Introduce RequestPartParameterProcessor to deal with @RequestPart annotations, adds parameters to MethodMetadata.formParams().
- Wrap HttpMessageConversionException in EncodeException.
- Add tests to verify expected behaviour.

However there still exists a gap in functionality where any user defined pojo will be serialized as a Map<String,String>, as there is currently no way for SpringFormEncoder to know about the Content-Type of the individual parts beyond MultipartFile, boxed primitive types (which are treated as text/plain) and other built-in types inherited from FormEncoder.
2020-01-23 15:02:21 +01:00
buildmaster 422dda0a75 Bumping versions 2020-01-21 14:06:45 +00:00
Olga Maciaszek-Sharma 3eabb0237e Add comment. 2020-01-20 17:32:19 +01:00
Olga Maciaszek-Sharma ada2cb7913 Revert "Add public delegate modifiers in order to avoid reflection in SC Sleuth."
This reverts commit 05b58e087e.
2020-01-20 17:31:13 +01:00
Olga Maciaszek-Sharma 05b58e087e Add public delegate modifiers in order to avoid reflection in SC Sleuth. 2020-01-20 17:00:56 +01:00
Olga Maciaszek-Sharma 80ecc9ac09 Make constructor public. Required for SC Sleuth gh-1528. 2020-01-20 14:28:30 +01:00
buildmaster bee97b8611 Bumping versions 2020-01-19 11:37:13 +00:00
matt62king 01d05227c8 Define custom AnnotatedParameterProcessors while keeping defaults, Fixes gh-111 (#268) 2020-01-13 21:32:17 -05:00
Aaron Whiteside 90400b9e43 Support for configuring the ExceptionPropagationPolicy (#261)
Can be configured via properties or as a `@Bean`.

Fixes GH-243
2020-01-13 20:02:40 -05:00
Michael Stummvoll b1e38db1e2 Update Javadoc for FeignClient.configuration() (#227)
Referring to @Configuration is a bit confusing. The referred class does not need the annotation, and actually using it could cause issues due the class additionally getting picked up by the component scan.
2020-01-13 15:56:47 -05:00
Olga Maciaszek-Sharma d77337bc79 Merge branch '2.1.x'
# Conflicts:
#	docs/pom.xml
#	pom.xml
#	spring-cloud-openfeign-core/pom.xml
#	spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java
#	spring-cloud-openfeign-dependencies/pom.xml
#	spring-cloud-starter-openfeign/pom.xml
2020-01-09 18:04:04 +01:00
Olga Maciaszek-SharmaandAhmad Mozafarnia 80d2067134 Merge fix 255 to 21x (#273)
* Fix bug in SpringEncoder (#225)

This commit fixes a bug in the SpringEncoder that causes the bodyType to
be ignored and not passed to generic message converters.

Fixes gh-225

* Fix after cherry-pick.

* Add Ahmad Mozafarnia to authors.

* Refactor tests.

Co-authored-by: Ahmad Mozafarnia <ahmad.mozafarnia@gmail.com>
2020-01-09 17:59:15 +01:00
Olga Maciaszek-SharmaandAhmad Mozafarnia 887ceefa60 Merge fix gh 225 (#272)
* Fix bug in SpringEncoder (#225)

This commit fixes a bug in the SpringEncoder that causes the bodyType to
be ignored and not passed to generic message converters.

Fixes gh-225

* Refactor changes based on Olga Maciaszek's review

* Add Ahmad Mozafarnia to authors.

Co-authored-by: Ahmad Mozafarnia <ahmad.mozafarnia@gmail.com>
2020-01-09 15:16:51 +01:00
buildmaster 2b364367cb Bumping versions 2020-01-08 11:37:49 +00:00
Olga Maciaszek-Sharma 8aa4729537 Upgrade to 10.7.0. (#270) 2020-01-07 16:12:14 +01:00
buildmaster 41e9b55869 Bumping versions 2020-01-02 11:38:31 +00:00
matt62king 8a25b8bd63 Support matrix variable (#260)
* support for matrix variable

* support for matrix variable Fixes gh-117

* unit tests for matrix variable

* Correct JavaDoc fixes gh-117
2020-01-01 19:54:55 +01:00
buildmaster 774017db85 Bumping versions 2019-12-21 11:38:25 +00:00
buildmaster d1b50ec2ae Bumping versions to 2.2.2.BUILD-SNAPSHOT after release 2019-12-20 12:51:11 +00:00
buildmaster bde804c6a4 Going back to snapshots 2019-12-20 12:51:11 +00:00
buildmaster f97cbb9a73 Bumping versions to 2.1.5.BUILD-SNAPSHOT after release 2019-11-18 15:11:21 +00:00
buildmaster de39e67e18 Going back to snapshots 2019-11-18 15:11:21 +00:00
buildmaster 78037a1161 Update SNAPSHOT to 2.1.4.RELEASE 2019-11-18 15:10:50 +00:00
31 changed files with 760 additions and 206 deletions
+2 -2
View File
@@ -5,12 +5,12 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.3.RELEASE</version>
<relativePath></relativePath>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Cloud OpenFeign</name>
<description>Spring Cloud OpenFeign</description>
+3 -3
View File
@@ -5,11 +5,11 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-docs</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Cloud OpenFeign Docs</name>
<description>Spring Cloud Docs</description>
@@ -100,7 +100,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
</parent>
<artifactId>spring-cloud-openfeign-docs</artifactId>
<packaging>pom</packaging>
+14
View File
@@ -0,0 +1,14 @@
:numbered!:
[appendix]
[[common-application-properties]]
== Common application properties
include::_attributes.adoc[]
Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches.
This appendix provides a list of common {project-full-name} properties and references to the underlying classes that consume them.
NOTE: Property contributions can come from additional jar files on your classpath, so you should not consider this an exhaustive list.
Also, you can define your own properties.
include::_configprops.adoc[]
@@ -524,6 +524,19 @@ public interface DemoTemplate {
}
----
=== Troubleshooting
==== Early Initialization Errors
Depending on how you are using your Feign clients you may see initialization errors when starting your application.
To work around this problem you can use an `ObjectProvider` when autowiring your client.
[source,java,indent=0]
----
@Autowired
ObjectProvider<TestFeginClient> testFeginClient;
----
== Configuration properties
To see the list of all Sleuth related configuration properties please check link:appendix.html[the Appendix page].
+4 -4
View File
@@ -4,14 +4,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Cloud OpenFeign</name>
<description>Spring Cloud OpenFeign</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.3.RELEASE</version>
<relativePath/>
</parent>
<scm>
@@ -26,8 +26,8 @@
<properties>
<main.basedir>${basedir}</main.basedir>
<jackson.version>2.7.3</jackson.version>
<spring-cloud-commons.version>2.2.1.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.2.1.RELEASE</spring-cloud-netflix.version>
<spring-cloud-commons.version>2.2.2.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.2.2.RELEASE</spring-cloud-netflix.version>
<!-- Plugin versions -->
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
+26 -33
View File
@@ -5,12 +5,12 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<relativePath>..</relativePath>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<name>Spring Cloud OpenFeign Core</name>
<description>Spring Cloud OpenFeign Core</description>
<url>https://spring.io/spring-cloud/spring-cloud-openfeign/spring-cloud-openfeign-core</url>
@@ -100,68 +100,68 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
<version>4.1.43.Final</version>
<version>4.1.45.Final</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.3.1.RELEASE</version>
<version>3.3.3.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@@ -175,27 +175,27 @@
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.4.RELEASE</version>
<version>1.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@@ -215,7 +215,7 @@
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>10.4.0</version>
<version>10.7.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@@ -228,35 +228,28 @@
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<version>10.4.0</version>
<version>10.7.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.4.0</version>
<version>10.7.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hystrix</artifactId>
<version>10.4.0</version>
<version>10.7.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>10.4.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-java8</artifactId>
<version>10.4.0</version>
<version>10.7.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@@ -356,35 +349,35 @@
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.4</version>
<version>3.14.7</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.2.3.RELEASE</version>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
+1 -6
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-openfeign-core</artifactId>
@@ -123,11 +123,6 @@
<artifactId>feign-okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-java8</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
@@ -85,7 +85,7 @@ public @interface FeignClient {
boolean decode404() default false;
/**
* A custom <code>@Configuration</code> for the feign client. Can contain override
* A custom configuration class for the feign client. Can contain override
* <code>@Bean</code> definition for the pieces that make up the client, for instance
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
@@ -21,6 +21,7 @@ import java.util.Objects;
import feign.Client;
import feign.Contract;
import feign.ExceptionPropagationPolicy;
import feign.Feign;
import feign.Logger;
import feign.QueryMapEncoder;
@@ -156,6 +157,11 @@ class FeignClientFactoryBean
if (this.decode404) {
builder.decode404();
}
ExceptionPropagationPolicy exceptionPropagationPolicy = getOptional(context,
ExceptionPropagationPolicy.class);
if (exceptionPropagationPolicy != null) {
builder.exceptionPropagationPolicy(exceptionPropagationPolicy);
}
}
protected void configureUsingProperties(
@@ -210,6 +216,10 @@ class FeignClientFactoryBean
if (Objects.nonNull(config.getContract())) {
builder.contract(getOrInstantiate(config.getContract()));
}
if (Objects.nonNull(config.getExceptionPropagationPolicy())) {
builder.exceptionPropagationPolicy(config.getExceptionPropagationPolicy());
}
}
private <T> T getOrInstantiate(Class<T> tClass) {
@@ -22,6 +22,7 @@ import java.util.Map;
import java.util.Objects;
import feign.Contract;
import feign.ExceptionPropagationPolicy;
import feign.Logger;
import feign.RequestInterceptor;
import feign.Retryer;
@@ -111,6 +112,8 @@ public class FeignClientProperties {
private Class<Contract> contract;
private ExceptionPropagationPolicy exceptionPropagationPolicy;
public Logger.Level getLoggerLevel() {
return this.loggerLevel;
}
@@ -192,6 +195,15 @@ public class FeignClientProperties {
this.contract = contract;
}
public ExceptionPropagationPolicy getExceptionPropagationPolicy() {
return exceptionPropagationPolicy;
}
public void setExceptionPropagationPolicy(
ExceptionPropagationPolicy exceptionPropagationPolicy) {
this.exceptionPropagationPolicy = exceptionPropagationPolicy;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -210,14 +222,17 @@ public class FeignClientProperties {
&& 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.contract, that.contract)
&& Objects.equals(this.exceptionPropagationPolicy,
that.exceptionPropagationPolicy);
}
@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.decode404, this.encoder, this.decoder, this.contract,
this.exceptionPropagationPolicy);
}
}
@@ -0,0 +1,83 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.openfeign.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.stream.Collectors;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.web.bind.annotation.MatrixVariable;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
/**
* {@link MatrixVariable} annotation processor.
*
* Can expand maps or single objects. Values are assigned from the objects
* {@code toString()} method.
*
* @author Matt King
* @see AnnotatedParameterProcessor
*/
public class MatrixVariableParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<MatrixVariable> ANNOTATION = MatrixVariable.class;
@Override
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
String name = ANNOTATION.cast(annotation).value();
checkState(emptyToNull(name) != null,
"MatrixVariable annotation was empty on param %s.",
context.getParameterIndex());
context.setParameterName(name);
if (Map.class.isAssignableFrom(parameterType)) {
data.indexToExpander().put(parameterIndex, this::expandMap);
}
else {
data.indexToExpander().put(parameterIndex,
object -> ";" + name + "=" + object.toString());
}
return true;
}
private String expandMap(Object object) {
Map<String, Object> paramMap = (Map) object;
return paramMap.keySet().stream()
.map(key -> ";" + key + "=" + paramMap.get(key).toString())
.collect(Collectors.joining());
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.openfeign.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.web.bind.annotation.RequestPart;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
/**
* {@link RequestPart} parameter processor.
*
* @author Aaron Whiteside
* @see AnnotatedParameterProcessor
*/
public class RequestPartParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<RequestPart> ANNOTATION = RequestPart.class;
@Override
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
MethodMetadata data = context.getMethodMetadata();
String name = ANNOTATION.cast(annotation).value();
checkState(emptyToNull(name) != null,
"RequestPart.value() was empty on parameter %s", parameterIndex);
context.setParameterName(name);
data.formParams().add(name);
Collection<String> names = context.setTemplateParameter(name,
data.indexToName().get(parameterIndex));
data.indexToName().put(parameterIndex, names);
return true;
}
}
@@ -47,7 +47,7 @@ public class FeignBlockingLoadBalancerClient implements Client {
private final BlockingLoadBalancerClient loadBalancerClient;
FeignBlockingLoadBalancerClient(Client delegate,
public FeignBlockingLoadBalancerClient(Client delegate,
BlockingLoadBalancerClient loadBalancerClient) {
this.delegate = delegate;
this.loadBalancerClient = loadBalancerClient;
@@ -73,11 +73,12 @@ public class FeignBlockingLoadBalancerClient implements Client {
String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri)
.toString();
Request newRequest = Request.create(request.httpMethod(), reconstructedUrl,
request.headers(), request.requestBody());
request.headers(), request.body(), request.charset(),
request.requestTemplate());
return delegate.execute(newRequest, options);
}
// Visible for Sleuth instrumentation
public Client getDelegate() {
return delegate;
}
@@ -134,7 +134,7 @@ public class FeignLoadBalancer extends
Map<String, Collection<String>> headers = new LinkedHashMap<>(
request.headers());
return Request.create(request.httpMethod(), getUri().toASCIIString(), headers,
request.requestBody());
request.body(), request.charset(), request.requestTemplate());
}
Request toRequest() {
@@ -40,6 +40,8 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.multipart.MultipartFile;
@@ -50,6 +52,8 @@ import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHead
/**
* @author Spencer Gibb
* @author Scien Jus
* @author Ahmad Mozafarnia
* @author Aaron Whiteside
*/
public class SpringEncoder implements Encoder {
@@ -57,7 +61,7 @@ public class SpringEncoder implements Encoder {
private final SpringFormEncoder springFormEncoder = new SpringFormEncoder();
private ObjectFactory<HttpMessageConverters> messageConverters;
private final ObjectFactory<HttpMessageConverters> messageConverters;
public SpringEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
this.messageConverters = messageConverters;
@@ -68,7 +72,6 @@ public class SpringEncoder implements Encoder {
throws EncodeException {
// template.body(conversionService.convert(object, String.class));
if (requestBody != null) {
Class<?> requestType = requestBody.getClass();
Collection<String> contentTypes = request.headers()
.get(HttpEncoding.CONTENT_TYPE);
@@ -78,44 +81,36 @@ public class SpringEncoder implements Encoder {
requestContentType = MediaType.valueOf(type);
}
if (bodyType != null && bodyType.equals(MultipartFile.class)) {
if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) {
this.springFormEncoder.encode(requestBody, bodyType, request);
return;
}
else {
String message = "Content-Type \"" + MediaType.MULTIPART_FORM_DATA
+ "\" not set for request body of type "
+ requestBody.getClass().getSimpleName();
throw new EncodeException(message);
if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) {
this.springFormEncoder.encode(requestBody, bodyType, request);
return;
}
else {
if (bodyType == MultipartFile.class) {
log.warn(
"For MultipartFile to be handled correctly, the 'consumes' parameter of @RequestMapping "
+ "should be specified as MediaType.MULTIPART_FORM_DATA_VALUE");
}
}
for (HttpMessageConverter<?> messageConverter : this.messageConverters
for (HttpMessageConverter messageConverter : this.messageConverters
.getObject().getConverters()) {
if (messageConverter.canWrite(requestType, requestContentType)) {
if (log.isDebugEnabled()) {
if (requestContentType != null) {
log.debug("Writing [" + requestBody + "] as \""
+ requestContentType + "\" using [" + messageConverter
+ "]");
}
else {
log.debug("Writing [" + requestBody + "] using ["
+ messageConverter + "]");
}
FeignOutputMessage outputMessage;
try {
if (messageConverter instanceof GenericHttpMessageConverter) {
outputMessage = checkAndWrite(requestBody, bodyType,
requestContentType,
(GenericHttpMessageConverter) messageConverter, request);
}
FeignOutputMessage outputMessage = new FeignOutputMessage(request);
try {
@SuppressWarnings("unchecked")
HttpMessageConverter<Object> copy = (HttpMessageConverter<Object>) messageConverter;
copy.write(requestBody, requestContentType, outputMessage);
}
catch (IOException ex) {
throw new EncodeException("Error converting request body", ex);
else {
outputMessage = checkAndWrite(requestBody, requestContentType,
messageConverter, request);
}
}
catch (IOException | HttpMessageConversionException ex) {
throw new EncodeException("Error converting request body", ex);
}
if (outputMessage != null) {
// clear headers
request.headers(null);
// converters can modify headers, so update the request
@@ -141,7 +136,7 @@ public class SpringEncoder implements Encoder {
}
}
String message = "Could not write request: no suitable HttpMessageConverter "
+ "found for request type [" + requestType.getName() + "]";
+ "found for request type [" + requestBody.getClass().getName() + "]";
if (requestContentType != null) {
message += " and content type [" + requestContentType + "]";
}
@@ -149,6 +144,49 @@ public class SpringEncoder implements Encoder {
}
}
@SuppressWarnings("unchecked")
private FeignOutputMessage checkAndWrite(Object body, MediaType contentType,
HttpMessageConverter converter, RequestTemplate request) throws IOException {
if (converter.canWrite(body.getClass(), contentType)) {
logBeforeWrite(body, contentType, converter);
FeignOutputMessage outputMessage = new FeignOutputMessage(request);
converter.write(body, contentType, outputMessage);
return outputMessage;
}
else {
return null;
}
}
@SuppressWarnings("unchecked")
private FeignOutputMessage checkAndWrite(Object body, Type genericType,
MediaType contentType, GenericHttpMessageConverter converter,
RequestTemplate request) throws IOException {
if (converter.canWrite(genericType, body.getClass(), contentType)) {
logBeforeWrite(body, contentType, converter);
FeignOutputMessage outputMessage = new FeignOutputMessage(request);
converter.write(body, genericType, contentType, outputMessage);
return outputMessage;
}
else {
return null;
}
}
private void logBeforeWrite(Object requestBody, MediaType requestContentType,
HttpMessageConverter messageConverter) {
if (log.isDebugEnabled()) {
if (requestContentType != null) {
log.debug("Writing [" + requestBody + "] as \"" + requestContentType
+ "\" using [" + messageConverter + "]");
}
else {
log.debug(
"Writing [" + requestBody + "] using [" + messageConverter + "]");
}
}
}
private final class FeignOutputMessage implements HttpOutputMessage {
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -36,10 +36,12 @@ import feign.Param;
import feign.Request;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.annotation.MatrixVariableParameterProcessor;
import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor;
import org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor;
import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor;
import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
import org.springframework.cloud.openfeign.annotation.RequestPartParameterProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.DefaultParameterNameDiscoverer;
@@ -68,6 +70,7 @@ import static org.springframework.core.annotation.AnnotatedElementUtils.findMerg
* @author Halvdan Hoem Grelland
* @author Aram Peres
* @author Olga Maciaszek-Sharma
* @author Aaron Whiteside
*/
public class SpringMvcContract extends Contract.BaseContract
implements ResourceLoaderAware {
@@ -110,13 +113,9 @@ public class SpringMvcContract extends Contract.BaseContract
"Parameter processors can not be null.");
Assert.notNull(conversionService, "ConversionService can not be null.");
List<AnnotatedParameterProcessor> processors;
if (!annotatedParameterProcessors.isEmpty()) {
processors = new ArrayList<>(annotatedParameterProcessors);
}
else {
processors = getDefaultAnnotatedArgumentsProcessors();
}
List<AnnotatedParameterProcessor> processors = getDefaultAnnotatedArgumentsProcessors();
processors.addAll(annotatedParameterProcessors);
this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
this.conversionService = conversionService;
this.convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
@@ -355,10 +354,12 @@ public class SpringMvcContract extends Contract.BaseContract
List<AnnotatedParameterProcessor> annotatedArgumentResolvers = new ArrayList<>();
annotatedArgumentResolvers.add(new MatrixVariableParameterProcessor());
annotatedArgumentResolvers.add(new PathVariableParameterProcessor());
annotatedArgumentResolvers.add(new RequestParamParameterProcessor());
annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());
annotatedArgumentResolvers.add(new QueryMapParameterProcessor());
annotatedArgumentResolvers.add(new RequestPartParameterProcessor());
return annotatedArgumentResolvers;
}
@@ -17,6 +17,7 @@
package org.springframework.cloud.openfeign;
import feign.Contract;
import feign.ExceptionPropagationPolicy;
import feign.Feign;
import feign.Logger;
import feign.QueryMapEncoder;
@@ -151,6 +152,14 @@ public class FeignClientOverrideDefaultsTests {
.isEqualTo(2);
}
@Test
public void exceptionPropagationPolicy() {
assertThat(this.context.getInstances("foo", ExceptionPropagationPolicy.class))
.isNull();
assertThat(this.context.getInstances("bar", ExceptionPropagationPolicy.class))
.containsValues(ExceptionPropagationPolicy.UNWRAP);
}
@FeignClient(name = "foo", url = "https://foo",
configuration = FooConfiguration.class)
interface FooClient {
@@ -252,6 +261,11 @@ public class FeignClientOverrideDefaultsTests {
return new BeanQueryMapEncoder();
}
@Bean
public ExceptionPropagationPolicy exceptionPropagationPolicy() {
return ExceptionPropagationPolicy.UNWRAP;
}
}
}
@@ -16,13 +16,14 @@
package org.springframework.cloud.openfeign;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.SocketTimeoutException;
import java.util.Collections;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import feign.Request;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.RetryableException;
@@ -50,7 +51,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@@ -78,6 +78,8 @@ public class FeignClientUsingPropertiesTests {
private FeignClientFactoryBean barFactoryBean;
private FeignClientFactoryBean unwrapFactoryBean;
private FeignClientFactoryBean formFactoryBean;
public FeignClientUsingPropertiesTests() {
@@ -89,6 +91,10 @@ public class FeignClientUsingPropertiesTests {
this.barFactoryBean.setContextId("bar");
this.barFactoryBean.setType(FeignClientFactoryBean.class);
this.unwrapFactoryBean = new FeignClientFactoryBean();
this.unwrapFactoryBean.setContextId("unwrap");
this.unwrapFactoryBean.setType(FeignClientFactoryBean.class);
this.formFactoryBean = new FeignClientFactoryBean();
this.formFactoryBean.setContextId("form");
this.formFactoryBean.setType(FeignClientFactoryBean.class);
@@ -106,6 +112,12 @@ public class FeignClientUsingPropertiesTests {
"http://localhost:" + this.port);
}
public UnwrapClient unwrapClient() {
this.unwrapFactoryBean.setApplicationContext(this.applicationContext);
return this.unwrapFactoryBean.feign(this.context).target(UnwrapClient.class,
"http://localhost:" + this.port);
}
public FormClient formClient() {
this.formFactoryBean.setApplicationContext(this.applicationContext);
return this.formFactoryBean.feign(this.context).target(FormClient.class,
@@ -124,6 +136,12 @@ public class FeignClientUsingPropertiesTests {
fail("it should timeout");
}
@Test(expected = SocketTimeoutException.class)
public void testUnwrap() throws Exception {
unwrapClient().unwrap();
fail("it should timeout");
}
@Test
public void testForm() {
Map<String, String> request = Collections.singletonMap("form", "Data");
@@ -145,6 +163,13 @@ public class FeignClientUsingPropertiesTests {
}
protected interface UnwrapClient {
@RequestMapping(method = RequestMethod.GET, value = "/bar") // intentionally /bar
String unwrap() throws IOException;
}
protected interface FormClient {
@RequestMapping(value = "/form", method = RequestMethod.POST,
@@ -233,7 +258,7 @@ public class FeignClientUsingPropertiesTests {
requestTemplate.header(HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_FORM_URLENCODED_VALUE);
requestTemplate.body(Request.Body.bodyTemplate(builder.toString(), UTF_8));
requestTemplate.body(builder.toString());
}
}
@@ -121,7 +121,7 @@ class FeignBlockingLoadBalancerClientTests {
assertThat(actualRequest.headers()).hasSize(1);
assertThat(actualRequest.headers()).containsEntry(HttpHeaders.CONTENT_TYPE,
Collections.singletonList(MediaType.APPLICATION_JSON_VALUE));
assertThat(actualRequest.requestBody().asString()).isEqualTo("hello");
assertThat(new String(actualRequest.body())).isEqualTo("hello");
}
private Request testRequest() {
@@ -130,8 +130,7 @@ class FeignBlockingLoadBalancerClientTests {
private Request testRequest(String host) {
return Request.create(Request.HttpMethod.GET, "http://" + host + "/path",
testHeaders(),
Request.Body.encoded("hello".getBytes(), StandardCharsets.UTF_8));
testHeaders(), "hello".getBytes(), StandardCharsets.UTF_8, null);
}
private Map<String, Collection<String>> testHeaders() {
@@ -159,7 +159,7 @@ public class FeignLoadBalancerTests {
@Test
public void testRibbonRequestURLEncode() throws Exception {
String url = "https://foo/?name=%7bcookie"; // name={cookie
Request request = Request.create(GET, url, new HashMap<>(), null, null);
Request request = Request.create(GET, url, new HashMap<>(), null, null, null);
assertThat(request.url()).isEqualTo(url);
@@ -133,7 +133,7 @@ public class RetryableFeignLoadBalancerTests {
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
@@ -151,7 +151,7 @@ public class RetryableFeignLoadBalancerTests {
@Test
public void executeNeverRetry() throws Exception {
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
@@ -215,7 +215,7 @@ public class RetryableFeignLoadBalancerTests {
}
};
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
@@ -260,7 +260,7 @@ public class RetryableFeignLoadBalancerTests {
}
};
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
@@ -310,7 +310,7 @@ public class RetryableFeignLoadBalancerTests {
}
};
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
@@ -339,7 +339,7 @@ public class RetryableFeignLoadBalancerTests {
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
@@ -365,7 +365,7 @@ public class RetryableFeignLoadBalancerTests {
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
@@ -446,7 +446,7 @@ public class RetryableFeignLoadBalancerTests {
}
};
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://listener"));
@@ -498,7 +498,7 @@ public class RetryableFeignLoadBalancerTests {
}
};
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://listener"));
@@ -535,7 +535,7 @@ public class RetryableFeignLoadBalancerTests {
}
};
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://listener"));
@@ -580,7 +580,7 @@ public class RetryableFeignLoadBalancerTests {
}
};
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
new byte[] {}, UTF_8, null);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
@@ -44,13 +44,12 @@ public class RibbonResponseStatusCodeExceptionTest {
@Test
public void getResponse() throws Exception {
Map<String, Collection<String>> headers = new HashMap<String, Collection<String>>();
List<String> fooValues = new ArrayList<String>();
Map<String, Collection<String>> headers = new HashMap<>();
List<String> fooValues = new ArrayList<>();
fooValues.add("bar");
headers.put("foo", fooValues);
Request request = Request.create(GET, "https://service.com",
new HashMap<String, Collection<String>>(), new byte[] {},
Charset.defaultCharset());
Request request = Request.create(GET, "https://service.com", new HashMap<>(),
new byte[] {}, Charset.defaultCharset(), null);
byte[] body = "foo".getBytes();
ByteArrayInputStream is = new ByteArrayInputStream(body);
Response response = Response.builder().status(200).reason("Success")
@@ -16,10 +16,11 @@
package org.springframework.cloud.openfeign.support;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import feign.RequestTemplate;
@@ -27,7 +28,6 @@ import feign.codec.EncodeException;
import feign.codec.Encoder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -35,12 +35,15 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.encoding.HttpEncoding;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractGenericHttpMessageConverter;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
@@ -60,6 +63,7 @@ import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
/**
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
* @author Ahmad Mozafarnia
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringEncoderTests.Application.class,
@@ -75,6 +79,10 @@ public class SpringEncoderTests {
@Qualifier("myHttpMessageConverter")
private HttpMessageConverter<?> myConverter;
@Autowired
@Qualifier("myGenericHttpMessageConverter")
private GenericHttpMessageConverter<?> myGenericConverter;
@Test
public void testCustomHttpMessageConverter() {
Encoder encoder = this.context.getInstance("foo", Encoder.class);
@@ -94,7 +102,35 @@ public class SpringEncoderTests {
assertThat(request.requestCharset()).as("request charset is null").isNotNull();
assertThat(request.requestCharset()).as("request charset is wrong")
.isEqualTo(Charset.forName("UTF-8"));
.isEqualTo(StandardCharsets.UTF_8);
}
// gh-225
@Test
public void testCustomGenericHttpMessageConverter() {
Encoder encoder = this.context.getInstance("foo", Encoder.class);
assertThat(encoder).isNotNull();
RequestTemplate request = new RequestTemplate();
ParameterizedTypeReference<List<String>> stringListType = new ParameterizedTypeReference<List<String>>() {
};
request.header(HttpEncoding.CONTENT_TYPE, "application/mygenerictype");
encoder.encode(Collections.singletonList("hi"), stringListType.getType(),
request);
Collection<String> contentTypeHeader = request.headers().get("Content-Type");
assertThat(contentTypeHeader).as("missing content type header").isNotNull();
assertThat(contentTypeHeader.isEmpty()).as("missing content type header")
.isFalse();
String header = contentTypeHeader.iterator().next();
assertThat(header).as("content type header is wrong")
.isEqualTo("application/mygenerictype");
assertThat(request.requestCharset()).as("request charset is null").isNotNull();
assertThat(request.requestCharset()).as("request charset is wrong")
.isEqualTo(StandardCharsets.UTF_8);
}
@Test
@@ -120,8 +156,6 @@ public class SpringEncoderTests {
MultipartFile multipartFile = new MockMultipartFile("test_multipart_file",
"hi".getBytes());
encoder.encode(multipartFile, MultipartFile.class, request);
assertThat(request.requestCharset()).as("request charset is not null").isNull();
}
// gh-105, gh-107
@@ -179,6 +213,11 @@ public class SpringEncoderTests {
return new MyHttpMessageConverter();
}
@Bean
GenericHttpMessageConverter<?> myGenericHttpMessageConverter() {
return new MyGenericHttpMessageConverter();
}
private static class MyHttpMessageConverter
extends AbstractGenericHttpMessageConverter<Object> {
@@ -204,20 +243,77 @@ public class SpringEncoderTests {
@Override
protected void writeInternal(Object o, Type type,
HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
throws HttpMessageNotWritableException {
}
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
throws HttpMessageNotReadableException {
return null;
}
@Override
public Object read(Type type, Class<?> contextClass,
HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
throws HttpMessageNotReadableException {
return null;
}
}
private static class MyGenericHttpMessageConverter
extends AbstractGenericHttpMessageConverter<Object> {
MyGenericHttpMessageConverter() {
super(new MediaType("application", "mygenerictype"));
}
private boolean isStringList(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
return parameterizedType.getRawType() == List.class
&& parameterizedType
.getActualTypeArguments()[0] == String.class;
}
else {
return false;
}
}
@Override
protected boolean supports(Class<?> clazz) {
return clazz == List.class;
}
@Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return canWrite(mediaType) && isStringList(type);
}
@Override
public boolean canRead(Type type, Class<?> contextClass,
MediaType mediaType) {
return canRead(mediaType) && isStringList(type);
}
@Override
protected void writeInternal(Object o, Type type,
HttpOutputMessage outputMessage)
throws HttpMessageNotWritableException {
}
@Override
public Object read(Type type, Class<?> contextClass,
HttpInputMessage inputMessage)
throws HttpMessageNotReadableException {
return null;
}
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws HttpMessageNotReadableException {
return null;
}
@@ -225,27 +321,4 @@ public class SpringEncoderTests {
}
class MediaTypeMatcher implements ArgumentMatcher<MediaType> {
private MediaType mediaType;
MediaTypeMatcher(String type, String subtype) {
this.mediaType = new MediaType(type, subtype);
}
@Override
public boolean matches(MediaType argument) {
return this.mediaType.equals(argument);
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("MediaTypeMatcher{");
sb.append("mediaType=").append(this.mediaType);
sb.append('}');
return sb.toString();
}
}
}
@@ -23,6 +23,7 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -45,6 +46,7 @@ import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -52,18 +54,19 @@ 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.RequestPart;
import org.springframework.web.multipart.MultipartFile;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
import static org.springframework.web.util.UriUtils.encode;
/**
* @author chadjaros
* @author Halvdan Hoem Grelland
* @author Aram Peres
* @author Aaron Whiteside
*/
public class SpringMvcContractTests {
@@ -244,7 +247,7 @@ public class SpringMvcContractTests {
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/advanced/test/{id}?amount=" + encode("{amount}", UTF_8));
.isEqualTo("/advanced/test/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -268,7 +271,7 @@ public class SpringMvcContractTests {
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/advanced/test/{id}?amount=" + encode("{amount}", UTF_8));
.isEqualTo("/advanced/test/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -293,7 +296,7 @@ public class SpringMvcContractTests {
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/advanced/test2?amount=" + encode("{amount}", UTF_8));
.isEqualTo("/advanced/test2?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -380,7 +383,7 @@ public class SpringMvcContractTests {
MethodMetadata data = this.contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/test?id=" + encode("{id}", UTF_8));
assertThat(data.template().url()).isEqualTo("/test?id=" + "{id}");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.template().queries().get("id").toString()).isEqualTo("[{id}]");
assertThat(data.indexToExpander().get(0)).isNotNull();
@@ -393,7 +396,7 @@ public class SpringMvcContractTests {
MethodMetadata data = this.contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/test?id=" + encode("{id}", UTF_8));
assertThat(data.template().url()).isEqualTo("/test?id=" + "{id}");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.template().queries().get("id").toString()).isEqualTo("[{id}]");
assertThat(data.indexToExpander().get(0)).isNotNull();
@@ -448,8 +451,8 @@ public class SpringMvcContractTests {
MethodMetadata data = this.contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo(
"/advanced/testfallback/{id}?amount=" + encode("{amount}", UTF_8));
assertThat(data.template().url())
.isEqualTo("/advanced/testfallback/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -493,8 +496,7 @@ public class SpringMvcContractTests {
MethodMetadata data = this.contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/queryMap?aParam=" + encode("{aParam}", UTF_8));
assertThat(data.template().url()).isEqualTo("/queryMap?aParam=" + "{aParam}");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.queryMapIndex().intValue()).isEqualTo(0);
Map<String, Collection<String>> params = data.template().queries();
@@ -509,7 +511,7 @@ public class SpringMvcContractTests {
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/queryMapObject?aParam=" + encode("{aParam}", UTF_8));
.isEqualTo("/queryMapObject?aParam=" + "{aParam}");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.queryMapIndex().intValue()).isEqualTo(0);
Map<String, Collection<String>> params = data.template().queries();
@@ -523,6 +525,35 @@ public class SpringMvcContractTests {
this.contract.parseAndValidateMetadata(method.getDeclaringClass(), method);
}
@Test
public void testMatrixVariable_MapParam() throws Exception {
Method method = TestTemplate_MatrixVariable.class
.getDeclaredMethod("matrixVariable", Map.class);
MethodMetadata data = this.contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
Map<String, String> testMap = new HashMap<>();
testMap.put("param", "value");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.template().url()).isEqualTo("/matrixVariable/{params}");
assertThat(";param=value")
.isEqualTo(data.indexToExpander().get(0).expand(testMap));
}
@Test
public void testMatrixVariable_ObjectParam() throws Exception {
Method method = TestTemplate_MatrixVariable.class
.getDeclaredMethod("matrixVariableObject", Object.class);
MethodMetadata data = this.contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.template().url()).isEqualTo("/matrixVariableObject/{param}");
assertThat(";param=value")
.isEqualTo(data.indexToExpander().get(0).expand("value"));
}
@Test
public void testAddingTemplatedParameterWithTheSameKey()
throws NoSuchMethodException {
@@ -535,6 +566,16 @@ public class SpringMvcContractTests {
"{Accept}");
}
@Test
public void testMultipleRequestPartAnnotations() throws NoSuchMethodException {
Method method = TestTemplate_RequestPart.class.getDeclaredMethod(
"requestWithMultipleParts", MultipartFile.class, String.class);
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.formParams()).contains("file", "id");
}
public interface TestTemplate_Simple {
@RequestMapping(value = "/test/{id}", method = RequestMethod.GET,
@@ -639,6 +680,25 @@ public class SpringMvcContractTests {
}
public interface TestTemplate_RequestPart {
@RequestMapping(path = "/requestPart", method = RequestMethod.POST,
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
void requestWithMultipleParts(@RequestPart("file") MultipartFile file,
@RequestPart("id") String identifier);
}
public interface TestTemplate_MatrixVariable {
@RequestMapping(path = "/matrixVariable/{params}")
String matrixVariable(@MatrixVariable("params") Map<String, Object> params);
@RequestMapping(path = "/matrixVariableObject/{param}")
String matrixVariableObject(@MatrixVariable("param") Object object);
}
@JsonAutoDetect
@RequestMapping("/advanced")
public interface TestTemplate_Advanced {
@@ -24,12 +24,19 @@ import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
@@ -43,9 +50,12 @@ import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Target;
import feign.codec.EncodeException;
import feign.hystrix.FallbackFactory;
import feign.hystrix.SetterFactory;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import rx.Observable;
import rx.Single;
@@ -74,7 +84,9 @@ import org.springframework.format.FormatterRegistry;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestBody;
@@ -82,15 +94,19 @@ 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.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
/**
* @author Spencer Gibb
* @author Jakub Narloch
* @author Erik Kringen
* @author Halvdan Hoem Grelland
* @author Aaron Whiteside
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignClientTests.Application.class,
@@ -110,6 +126,9 @@ public class FeignClientTests {
public static final String MYHEADER2 = "myheader2";
@Rule
public ExpectedException expected = ExpectedException.none();
@Autowired
HystrixClient hystrixClient;
@@ -144,6 +163,9 @@ public class FeignClientTests {
@Autowired
private NullHystrixClientWithFallBackFactory nullHystrixClientWithFallBackFactory;
@Autowired
private MultipartClient multipartClient;
private static ArrayList<Hello> getHelloList() {
ArrayList<Hello> hellos = new ArrayList<>();
hellos.add(new Hello(HELLO_WORLD_1));
@@ -422,6 +444,75 @@ public class FeignClientTests {
assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos);
}
@Test
public void testSingleRequestPart() {
String response = this.multipartClient.singlePart("abc");
assertThat(response).isEqualTo("abc");
}
@Test
public void testMultipleRequestParts() {
MockMultipartFile file = new MockMultipartFile("file", "hello.bin", null,
"hello".getBytes());
String response = this.multipartClient.multipart("abc", "123", file);
assertThat(response).isEqualTo("abc123hello.bin");
}
@Test
public void testRequestPartWithListOfMultipartFiles() {
List<MultipartFile> multipartFiles = Arrays.asList(
new MockMultipartFile("file1", "hello1.bin", null, "hello".getBytes()),
new MockMultipartFile("file2", "hello2.bin", null, "hello".getBytes()));
String partNames = this.multipartClient
.requestPartListOfMultipartFilesReturnsPartNames(multipartFiles);
assertThat(partNames).isEqualTo("files,files");
String fileNames = this.multipartClient
.requestPartListOfMultipartFilesReturnsFileNames(multipartFiles);
assertThat(fileNames).contains("hello1.bin", "hello2.bin");
}
@Test
public void testRequestBodyWithSingleMultipartFile() {
String partName = UUID.randomUUID().toString();
MockMultipartFile file1 = new MockMultipartFile(partName, "hello1.bin", null,
"hello".getBytes());
String response = this.multipartClient.requestBodySingleMultipartFile(file1);
assertThat(response).isEqualTo(partName);
}
@Test
public void testRequestBodyWithListOfMultipartFiles() {
MockMultipartFile file1 = new MockMultipartFile("file1", "hello1.bin", null,
"hello".getBytes());
MockMultipartFile file2 = new MockMultipartFile("file2", "hello2.bin", null,
"hello".getBytes());
String response = this.multipartClient
.requestBodyListOfMultipartFiles(Arrays.asList(file1, file2));
assertThat(response).contains("file1", "file2");
}
@Test
public void testRequestBodyWithMap() {
MockMultipartFile file1 = new MockMultipartFile("file1", "hello1.bin", null,
"hello".getBytes());
MockMultipartFile file2 = new MockMultipartFile("file2", "hello2.bin", null,
"hello".getBytes());
Map<String, Object> form = new HashMap<>();
form.put("file1", file1);
form.put("file2", file2);
form.put("hello", "world");
String response = this.multipartClient.requestBodyMap(form);
assertThat(response).contains("file1", "file2", "hello");
}
@Test
public void testInvalidMultipartFile() {
MockMultipartFile file = new MockMultipartFile("file1", "hello1.bin", null,
"hello".getBytes());
expected.expectCause(instanceOf(EncodeException.class));
this.multipartClient.invalid(file);
}
protected enum Arg {
A, B;
@@ -572,6 +663,55 @@ public class FeignClientTests {
}
@FeignClient(name = "localapp8")
protected interface MultipartClient {
@RequestMapping(method = RequestMethod.POST, path = "/singlePart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String singlePart(@RequestPart("hello") String hello);
@RequestMapping(method = RequestMethod.POST, path = "/multipart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String multipart(@RequestPart("hello") String hello,
@RequestPart("world") String world,
@RequestPart("file") MultipartFile file);
@RequestMapping(method = RequestMethod.POST, path = "/multipartNames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String requestPartListOfMultipartFilesReturnsPartNames(
@RequestPart("files") List<MultipartFile> files);
@RequestMapping(method = RequestMethod.POST, path = "/multipartFilenames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String requestPartListOfMultipartFilesReturnsFileNames(
@RequestPart("files") List<MultipartFile> files);
@RequestMapping(method = RequestMethod.POST, path = "/multipartNames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String requestBodyListOfMultipartFiles(@RequestBody List<MultipartFile> files);
@RequestMapping(method = RequestMethod.POST, path = "/multipartNames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String requestBodySingleMultipartFile(@RequestBody MultipartFile file);
@RequestMapping(method = RequestMethod.POST, path = "/multipartNames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String requestBodyMap(@RequestBody Map<String, ?> form);
@RequestMapping(method = RequestMethod.POST, path = "/invalid",
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String invalid(@RequestBody MultipartFile file);
}
protected static class OtherArg {
public final String value;
@@ -706,7 +846,7 @@ public class FeignClientTests {
DecodingTestClient.class, HystrixClient.class,
HystrixClientWithFallBackFactory.class, HystrixSetterFactoryClient.class,
InvalidTypeHystrixClientWithFallBackFactory.class,
NullHystrixClientWithFallBackFactory.class },
NullHystrixClientWithFallBackFactory.class, MultipartClient.class },
defaultConfiguration = TestDefaultFeignConfig.class)
@RibbonClients({
@RibbonClient(name = "localapp",
@@ -724,6 +864,8 @@ public class FeignClientTests {
@RibbonClient(name = "localapp6",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp7",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp8",
configuration = LocalRibbonClientConfiguration.class) })
@Import(NoSecurityConfiguration.class)
protected static class Application {
@@ -884,6 +1026,38 @@ public class FeignClientTests {
return result;
}
@RequestMapping(method = RequestMethod.POST, path = "/singlePart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String multipart(@RequestPart("hello") String hello) {
return hello;
}
@RequestMapping(method = RequestMethod.POST, path = "/multipart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String multipart(@RequestPart("hello") String hello,
@RequestPart("world") String world,
@RequestPart("file") MultipartFile file) {
return hello + world + file.getOriginalFilename();
}
@RequestMapping(method = RequestMethod.POST, path = "/multipartNames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String multipartNames(HttpServletRequest request) throws Exception {
return request.getParts().stream().map(Part::getName)
.collect(Collectors.joining(","));
}
@RequestMapping(method = RequestMethod.POST, path = "/multipartFilenames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
String multipartFilenames(HttpServletRequest request) throws Exception {
return request.getParts().stream().map(Part::getSubmittedFileName)
.collect(Collectors.joining(","));
}
}
public static class Hello {
@@ -13,3 +13,6 @@ feign.client.config.foo.requestInterceptors[1]=org.springframework.cloud.openfei
feign.client.config.bar.connectTimeout=1000
feign.client.config.bar.readTimeout=1000
feign.client.config.form.encoder=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FormEncoder
feign.client.config.unwrap.connectTimeout=1000
feign.client.config.unwrap.readTimeout=1000
feign.client.config.unwrap.exceptionPropagationPolicy=unwrap
@@ -5,12 +5,12 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.3.RELEASE</version>
<relativePath></relativePath>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<packaging>pom</packaging>
<name>spring-cloud-openfeign-dependencies</name>
<description>Spring Cloud OpenFeign Dependencies</description>
@@ -80,13 +80,13 @@
</snapshotRepository>
<site>
<id>spring-docs</id>
<url>scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-cloud/docs/spring-cloud-dependencies-parent/2.2.1.RELEASE/spring-cloud-openfeign-dependencies</url>
<url>scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-cloud/docs/spring-cloud-dependencies-parent/2.2.3.RELEASE/spring-cloud-openfeign-dependencies</url>
</site>
<downloadUrl>https://github.com/spring-cloud</downloadUrl>
</distributionManagement>
<properties>
<feign-form.version>3.8.0</feign-form.version>
<feign.version>10.4.0</feign.version>
<feign.version>10.7.4</feign.version>
</properties>
<dependencyManagement>
<dependencies>
@@ -125,11 +125,6 @@
<artifactId>feign-hystrix</artifactId>
<version>${feign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-java8</artifactId>
<version>${feign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
+3 -8
View File
@@ -6,16 +6,16 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.2.1.RELEASE</version>
<version>2.2.3.RELEASE</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<packaging>pom</packaging>
<name>spring-cloud-openfeign-dependencies</name>
<description>Spring Cloud OpenFeign Dependencies</description>
<properties>
<feign.version>10.4.0</feign.version>
<feign.version>10.7.4</feign.version>
<feign-form.version>3.8.0</feign-form.version>
</properties>
<dependencyManagement>
@@ -55,11 +55,6 @@
<artifactId>feign-hystrix</artifactId>
<version>${feign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-java8</artifactId>
<version>${feign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
@@ -5,12 +5,12 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<relativePath>..</relativePath>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<name>Spring Cloud Starter OpenFeign</name>
<description>Spring Cloud Starter OpenFeign</description>
<url>https://projects.spring.io/spring-cloud</url>
@@ -100,69 +100,63 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.2.RELEASE</version>
<version>5.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>10.4.0</version>
<version>10.7.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<version>10.4.0</version>
<version>10.7.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hystrix</artifactId>
<version>10.4.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-java8</artifactId>
<version>10.4.0</version>
<version>10.7.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
+1 -5
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.2.RELEASE</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-cloud-starter-openfeign</artifactId>
@@ -48,10 +48,6 @@
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hystrix</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-java8</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>