diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml
index 481f140c..8692b734 100644
--- a/spring-cloud-openfeign-core/pom.xml
+++ b/spring-cloud-openfeign-core/pom.xml
@@ -93,6 +93,10 @@
feign-core
true
+
+ io.github.openfeign.form
+ feign-form-spring
+
io.github.openfeign
feign-slf4j
@@ -168,6 +172,11 @@
spring-boot-autoconfigure-processor
true
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
org.springframework.boot
spring-boot-starter-test
diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/HystrixTargeter.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/HystrixTargeter.java
index d9925043..c2a488f5 100644
--- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/HystrixTargeter.java
+++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/HystrixTargeter.java
@@ -62,19 +62,6 @@ class HystrixTargeter implements Targeter {
Class> fallbackFactoryClass) {
FallbackFactory extends T> fallbackFactory = (FallbackFactory extends T>)
getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
- /* We take a sample fallback from the fallback factory to check if it returns a fallback
- that is compatible with the annotated feign interface. */
- Object exampleFallback = fallbackFactory.create(new RuntimeException());
- Assert.notNull(exampleFallback,
- String.format(
- "Incompatible fallbackFactory instance for feign client %s. Factory may not produce null!",
- feignClientName));
- if (!target.type().isAssignableFrom(exampleFallback.getClass())) {
- throw new IllegalStateException(
- String.format(
- "Incompatible fallbackFactory instance for feign client %s. Factory produces instances of '%s', but should produce instances of '%s'",
- feignClientName, exampleFallback.getClass(), target.type()));
- }
return builder.target(target, fallbackFactory);
}
diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java
index 0ee52016..fa4508ab 100644
--- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java
+++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java
@@ -24,6 +24,7 @@ import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
+import java.util.Objects;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -35,10 +36,12 @@ import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
+import org.springframework.web.multipart.MultipartFile;
import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
+import feign.form.spring.SpringFormEncoder;
import static org.springframework.cloud.openfeign.support.FeignUtils.getHeaders;
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
@@ -51,6 +54,8 @@ public class SpringEncoder implements Encoder {
private static final Log log = LogFactory.getLog(SpringEncoder.class);
+ private final SpringFormEncoder springFormEncoder = new SpringFormEncoder();
+
private ObjectFactory messageConverters;
public SpringEncoder(ObjectFactory messageConverters) {
@@ -71,6 +76,18 @@ public class SpringEncoder implements Encoder {
requestContentType = MediaType.valueOf(type);
}
+ if (bodyType != null && bodyType.equals(MultipartFile.class)) {
+ if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) {
+ 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);
+ }
+ }
+
for (HttpMessageConverter> messageConverter : this.messageConverters
.getObject().getConverters()) {
if (messageConverter.canWrite(requestType, requestContentType)) {
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/invalid/FeignClientValidationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/invalid/FeignClientValidationTests.java
index 9d4ca565..84dfe1c2 100644
--- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/invalid/FeignClientValidationTests.java
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/invalid/FeignClientValidationTests.java
@@ -240,44 +240,4 @@ public class FeignClientValidationTests {
}
}
-
- @Test
- public void testWrongFallbackFactoryGenericType() {
- try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
- WrongFallbackFactoryGenericTypeConfiguration.class)) {
- this.expected.expectMessage("Incompatible fallbackFactory instance");
- assertNotNull(context.getBean(WrongFallbackFactoryGenericTypeConfiguration.Client.class));
- }
- }
-
- @Configuration
- @Import(FeignAutoConfiguration.class)
- @EnableFeignClients(clients = WrongFallbackFactoryGenericTypeConfiguration.Client.class)
- protected static class WrongFallbackFactoryGenericTypeConfiguration {
-
- @FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = ClientFallback.class)
- interface Client {
- @RequestMapping(method = RequestMethod.GET, value = "/")
- String get();
- }
-
- @Bean
- ClientFallback dummy() {
- return new ClientFallback();
- }
-
- class ClientFallback implements FallbackFactory {
-
- @Override
- public String create(Throwable cause) {
- return "tryinToTrickYa";
- }
- }
-
- @Bean
- public Feign.Builder feignBuilder() {
- return HystrixFeign.builder();
- }
-
- }
}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java
index f3672d94..fcc9b53e 100644
--- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java
@@ -40,9 +40,11 @@ import org.springframework.http.converter.AbstractGenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
+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.RestController;
+import org.springframework.web.multipart.MultipartFile;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
@@ -50,6 +52,7 @@ import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import feign.RequestTemplate;
+import feign.codec.EncodeException;
/**
* @author Spencer Gibb
@@ -96,6 +99,31 @@ public class SpringEncoderTests {
assertThat("request charset is not null", request.charset(), is(nullValue()));
}
+
+ @Test(expected = EncodeException.class)
+ public void testMultipartFile1() {
+ SpringEncoder encoder = this.context.getInstance("foo", SpringEncoder.class);
+ assertThat(encoder, is(notNullValue()));
+ RequestTemplate request = new RequestTemplate();
+
+ MultipartFile multipartFile = new MockMultipartFile("test_multipart_file", "hi".getBytes());
+ encoder.encode(multipartFile, MultipartFile.class, request);
+
+ assertThat("request charset is not null", request.charset(), is(nullValue()));
+ }
+
+ @Test
+ public void testMultipartFile2() {
+ SpringEncoder encoder = this.context.getInstance("foo", SpringEncoder.class);
+ assertThat(encoder, is(notNullValue()));
+ RequestTemplate request = new RequestTemplate();
+ request = request.header("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE);
+
+ MultipartFile multipartFile = new MockMultipartFile("test_multipart_file", "hi".getBytes());
+ encoder.encode(multipartFile, MultipartFile.class, request);
+
+ assertThat("request charset is not null", request.charset(), is(nullValue()));
+ }
class MediaTypeMatcher implements ArgumentMatcher {
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientTests.java
index d4c55b2b..d5b76832 100644
--- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientTests.java
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientTests.java
@@ -66,6 +66,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
+import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
@@ -129,6 +130,12 @@ public class FeignClientTests {
@Autowired
private HystrixClientWithFallBackFactory hystrixClientWithFallBackFactory;
+ @Autowired
+ private InvalidTypeHystrixClientWithFallBackFactory invalidTypeHystrixClientWithFallBackFactory;
+
+ @Autowired
+ private NullHystrixClientWithFallBackFactory nullHystrixClientWithFallBackFactory;
+
@Autowired
@Qualifier("localapp3FeignClient")
HystrixClient namedHystrixClient;
@@ -275,6 +282,20 @@ public class FeignClientTests {
Hello fail();
}
+ @FeignClient(name = "localapp6", fallbackFactory = InvalidTypeHystrixClientFallbackFactory.class)
+ protected interface InvalidTypeHystrixClientWithFallBackFactory {
+
+ @RequestMapping(method = RequestMethod.GET, path = "/fail")
+ Hello fail();
+ }
+
+ @FeignClient(name = "localapp7", fallbackFactory = NullHystrixClientFallbackFactory.class)
+ protected interface NullHystrixClientWithFallBackFactory {
+
+ @RequestMapping(method = RequestMethod.GET, path = "/fail")
+ Hello fail();
+ }
+
static class HystrixClientFallbackFactory implements FallbackFactory {
@Override
@@ -289,6 +310,22 @@ public class FeignClientTests {
}
}
+ static class InvalidTypeHystrixClientFallbackFactory implements FallbackFactory {
+
+ @Override
+ public String create(final Throwable cause) {
+ return "hello";
+ }
+ }
+
+ static class NullHystrixClientFallbackFactory implements FallbackFactory {
+
+ @Override
+ public String create(final Throwable cause) {
+ return null;
+ }
+ }
+
static class HystrixClientFallback implements HystrixClient {
@Override
public Hello fail() {
@@ -348,7 +385,8 @@ public class FeignClientTests {
@RestController
@EnableFeignClients(clients = { TestClientServiceId.class, TestClient.class,
DecodingTestClient.class, HystrixClient.class, HystrixClientWithFallBackFactory.class,
- HystrixSetterFactoryClient.class},
+ HystrixSetterFactoryClient.class, InvalidTypeHystrixClientWithFallBackFactory.class,
+ NullHystrixClientWithFallBackFactory.class},
defaultConfiguration = TestDefaultFeignConfig.class)
@RibbonClients({
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class),
@@ -356,7 +394,9 @@ public class FeignClientTests {
@RibbonClient(name = "localapp2", configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp4", configuration = LocalRibbonClientConfiguration.class),
- @RibbonClient(name = "localapp5", configuration = LocalRibbonClientConfiguration.class)
+ @RibbonClient(name = "localapp5", configuration = LocalRibbonClientConfiguration.class),
+ @RibbonClient(name = "localapp6", configuration = LocalRibbonClientConfiguration.class),
+ @RibbonClient(name = "localapp7", configuration = LocalRibbonClientConfiguration.class)
})
protected static class Application {
@@ -371,6 +411,16 @@ public class FeignClientTests {
return new HystrixClientFallbackFactory();
}
+ @Bean
+ public InvalidTypeHystrixClientFallbackFactory invalidTypeHystrixClientFallbackFactory() {
+ return new InvalidTypeHystrixClientFallbackFactory();
+ }
+
+ @Bean
+ public NullHystrixClientFallbackFactory nullHystrixClientFallbackFactory() {
+ return new NullHystrixClientFallbackFactory();
+ }
+
@Bean
FeignFormatterRegistrar feignFormatterRegistrar() {
return new FeignFormatterRegistrar() {
@@ -722,6 +772,16 @@ public class FeignClientTests {
hello.getMessage().contains("500"));
}
+ @Test(expected = HystrixRuntimeException.class)
+ public void testInvalidTypeHystrixFallbackFactory() throws Exception {
+ invalidTypeHystrixClientWithFallBackFactory.fail();
+ }
+
+ @Test(expected = HystrixRuntimeException.class)
+ public void testNullHystrixFallbackFactory() throws Exception {
+ nullHystrixClientWithFallBackFactory.fail();
+ }
+
@Test
public void namedFeignClientWorks() {
assertNotNull("namedHystrixClient was null", this.namedHystrixClient);
diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml
index 8602c1ab..57128200 100644
--- a/spring-cloud-openfeign-dependencies/pom.xml
+++ b/spring-cloud-openfeign-dependencies/pom.xml
@@ -15,6 +15,7 @@
Spring Cloud OpenFeign Dependencies
9.7.0
+ 3.3.0
@@ -33,6 +34,11 @@
feign-core
${feign.version}
+
+ io.github.openfeign.form
+ feign-form-spring
+ ${feign-form.version}
+
io.github.openfeign
feign-slf4j