Merge branch '2.0.x' of github.com:spring-cloud/spring-cloud-openfeign into 2.0.x

This commit is contained in:
Ryan Baxter
2018-09-12 15:00:42 -04:00
7 changed files with 122 additions and 55 deletions

View File

@@ -93,6 +93,10 @@
<artifactId>feign-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
@@ -168,6 +172,11 @@
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -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);
}

View File

@@ -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<HttpMessageConverters> messageConverters;
public SpringEncoder(ObjectFactory<HttpMessageConverters> 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)) {

View File

@@ -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<String> {
@Override
public String create(Throwable cause) {
return "tryinToTrickYa";
}
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
}
}

View File

@@ -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<MediaType> {

View File

@@ -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<HystrixClientWithFallBackFactory> {
@Override
@@ -289,6 +310,22 @@ public class FeignClientTests {
}
}
static class InvalidTypeHystrixClientFallbackFactory implements FallbackFactory<String> {
@Override
public String create(final Throwable cause) {
return "hello";
}
}
static class NullHystrixClientFallbackFactory implements FallbackFactory<String> {
@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);

View File

@@ -15,6 +15,7 @@
<description>Spring Cloud OpenFeign Dependencies</description>
<properties>
<feign.version>9.7.0</feign.version>
<feign-form.version>3.3.0</feign-form.version>
</properties>
<dependencyManagement>
<dependencies>
@@ -33,6 +34,11 @@
<artifactId>feign-core</artifactId>
<version>${feign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>${feign-form.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>