Merge remote-tracking branch 'origin/main' into 3.1.x

This commit is contained in:
Olga MaciaszekSharma
2021-07-09 12:03:00 +02:00
11 changed files with 200 additions and 112 deletions

View File

@@ -32,4 +32,7 @@ jobs:
restore-keys: |
${{ runner.os }}-maven-
- name: Build with Maven
run: ./mvnw clean install -B -U
run: ./mvnw clean install -B -U -P sonar
- uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true

View File

@@ -26,7 +26,7 @@
<properties>
<main.basedir>${basedir}</main.basedir>
<jackson.version>2.11.3</jackson.version>
<spring-cloud-commons.version>3.0.3-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-commons.version>3.1.0-SNAPSHOT</spring-cloud-commons.version>
<!-- Plugin versions -->
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>

View File

@@ -382,11 +382,9 @@ public class FeignClientFactoryBean
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
if (url != null && LOG.isWarnEnabled()) {
LOG.warn("The provided URL is empty. Will try picking an instance via load-balancing.");
}
else if (LOG.isDebugEnabled()) {
LOG.debug("URL not provided. Will use LoadBalancer.");
if (LOG.isInfoEnabled()) {
LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
}
if (!name.startsWith("http")) {
url = "http://" + name;

View File

@@ -72,8 +72,8 @@ public class MatrixVariableParameterProcessor implements AnnotatedParameterProce
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());
return paramMap.keySet().stream().filter(key -> paramMap.get(key) != null)
.map(key -> ";" + key + "=" + paramMap.get(key).toString()).collect(Collectors.joining());
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2013-2020 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.loadbalancer;
import java.io.ByteArrayInputStream;
import java.net.URI;
import feign.Response;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
/**
* A {@link RetryableStatusCodeException} for {@link Response}s.
*
* @author Ryan Baxter
*/
public class LoadBalancerResponseStatusCodeException extends RetryableStatusCodeException {
private final Response response;
public LoadBalancerResponseStatusCodeException(String serviceId, Response response, byte[] body, URI uri) {
super(serviceId, response.status(), response, uri);
this.response = Response.builder().body(new ByteArrayInputStream(body), body.length).headers(response.headers())
.reason(response.reason()).status(response.status()).request(response.request()).build();
}
@Override
public Response getResponse() {
return this.response;
}
}

View File

@@ -46,7 +46,6 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycleValida
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.ResponseData;
import org.springframework.cloud.client.loadbalancer.RetryableRequestContext;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -57,6 +56,7 @@ import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import static org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils.buildRequestData;
@@ -161,8 +161,11 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Retrying on status code: %d", responseStatus));
}
byte[] byteArray = response.body() == null ? new byte[] {}
: StreamUtils.copyToByteArray(response.body().asInputStream());
response.close();
throw new RetryableStatusCodeException(serviceId, responseStatus, response, URI.create(request.url()));
throw new LoadBalancerResponseStatusCodeException(serviceId, response, byteArray,
URI.create(request.url()));
}
return response;
}, new LoadBalancedRecoveryCallback<Response, Response>() {

View File

@@ -25,6 +25,7 @@ import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import feign.RequestTemplate;
@@ -50,6 +51,10 @@ import org.springframework.web.multipart.MultipartFile;
import static org.springframework.cloud.openfeign.support.FeignUtils.getHeaders;
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import static org.springframework.http.MediaType.MULTIPART_MIXED;
import static org.springframework.http.MediaType.MULTIPART_RELATED;
/**
* @author Spencer Gibb
@@ -58,6 +63,7 @@ import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHead
* @author Aaron Whiteside
* @author Darren Foong
* @author Olga Maciaszek-Sharma
* @author Can Bezmen
*/
@SuppressWarnings("rawtypes")
public class SpringEncoder implements Encoder {
@@ -115,7 +121,7 @@ public class SpringEncoder implements Encoder {
requestContentType = MediaType.valueOf(type);
}
if (isMultipartType(requestContentType)) {
if (isFormRelatedContentType(requestContentType)) {
springFormEncoder.encode(requestBody, bodyType, request);
return;
}
@@ -229,9 +235,16 @@ public class SpringEncoder implements Encoder {
}
}
private boolean isFormRelatedContentType(MediaType requestContentType) {
return isMultipartType(requestContentType) || isFormUrlEncoded(requestContentType);
}
private boolean isMultipartType(MediaType requestContentType) {
return Arrays.asList(MediaType.MULTIPART_FORM_DATA, MediaType.MULTIPART_MIXED, MediaType.MULTIPART_RELATED)
.contains(requestContentType);
return Arrays.asList(MULTIPART_FORM_DATA, MULTIPART_MIXED, MULTIPART_RELATED).contains(requestContentType);
}
private boolean isFormUrlEncoded(MediaType requestContentType) {
return Objects.equals(APPLICATION_FORM_URLENCODED, requestContentType);
}
private boolean binaryContentType(FeignOutputMessage outputMessage) {

View File

@@ -16,7 +16,10 @@
package org.springframework.cloud.openfeign.loadbalancer;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
@@ -29,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
import feign.Client;
import feign.Request;
import feign.Response;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -117,6 +121,13 @@ class RetryableFeignBlockingLoadBalancerClientTests {
return Response.builder().request(testRequest()).status(status).build();
}
private Response testResponse(int status, String body) {
// ByteArrayInputStream ignores close() and must be wrapped
InputStream reallyCloseable = new BufferedInputStream(
new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)));
return Response.builder().request(testRequest()).status(status).body(reallyCloseable, null).build();
}
@Test
void shouldExecuteOriginalRequestIfInstanceNotFound() throws IOException {
Request request = testRequest();
@@ -148,6 +159,22 @@ class RetryableFeignBlockingLoadBalancerClientTests {
verify(delegate, times(2)).execute(any(), any());
}
@Test
void shouldExposeResponseBodyOnRetry() throws IOException {
properties.getRetry().getRetryableStatusCodes().add(503);
Request request = testRequest();
when(delegate.execute(any(), any())).thenReturn(testResponse(503, "foo"), testResponse(503, "foo"));
when(retryFactory.createRetryPolicy(any(), eq(loadBalancerClient)))
.thenReturn(new BlockingLoadBalancedRetryPolicy(properties));
when(loadBalancerClient.reconstructURI(serviceInstance, URI.create("http://test/path")))
.thenReturn(URI.create("http://testhost:80/path"));
Response response = feignBlockingLoadBalancerClient.execute(request, new Request.Options());
String bodyContent = IOUtils.toString(response.body().asReader(StandardCharsets.UTF_8));
assertThat(bodyContent).isEqualTo("foo");
}
@Test
void shouldPassCorrectRequestToDelegate() throws IOException {
Request request = testRequest();

View File

@@ -60,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.HttpHeaders.ACCEPT;
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
@@ -68,6 +69,7 @@ import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
* @author Ahmad Mozafarnia
* @author Can Bezmen
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringEncoderTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
@@ -179,6 +181,17 @@ public class SpringEncoderTests {
assertThat(new String(request.requestBody().asBytes())).as("Body content cannot be decoded").contains("hi");
}
@Test
public void testFromURLEncodedValue() {
Encoder encoder = context.getInstance("formUrlEncoded", Encoder.class);
assertThat(encoder).isNotNull();
RequestTemplate request = new RequestTemplate();
request.header(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE);
String body = "test";
encoder.encode(body, String.class, request);
assertThat(new String(request.requestBody().asBytes())).as("Body content cannot be decoded").contains(body);
}
@Test
public void testNoCharsetForBinaryFiles() {
Encoder encoder = context.getInstance("test", Encoder.class);

View File

@@ -38,7 +38,6 @@ import javax.servlet.http.Part;
import feign.Client;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.codec.EncodeException;
import org.junit.jupiter.api.Test;
@@ -65,7 +64,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
@@ -73,6 +71,8 @@ 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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -84,6 +84,9 @@ import org.springframework.web.multipart.MultipartFile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
/**
* @author Spencer Gibb
@@ -277,6 +280,13 @@ class ValidFeignClientTests {
assertThat(testClient.getToString(args)).isEqualTo(expectedResult);
}
@Test
void testFormURLEncoded() {
Hello hello = new Hello(HELLO_WORLD_1);
Hello response = testClient.postFormUrlEncoded(hello);
assertThat(response).isEqualTo(hello);
}
@Test
void namedFeignClientWorks() {
assertThat(namedFeignClient).as("namedFeignClient was null").isNotNull();
@@ -382,52 +392,42 @@ class ValidFeignClientTests {
@FeignClient(name = "localapp8")
protected interface MultipartClient {
@RequestMapping(method = RequestMethod.POST, path = "/singlePart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/singlePart", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String singlePart(@RequestPart("hello") String hello);
@RequestMapping(method = RequestMethod.POST, path = "/singlePojoPart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/singlePojoPart", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String singlePojoPart(@RequestPart("hello") Hello hello);
@RequestMapping(method = RequestMethod.POST, path = "/multipart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipart", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String multipart(@RequestPart("hello") String hello, @RequestPart("world") String world,
@RequestPart("file") MultipartFile file);
@RequestMapping(method = RequestMethod.POST, path = "/multipartPojo",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipartPojo", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String multipartPojo(@RequestPart("hello") String hello, @RequestPart("world") String world,
@RequestPart("pojo1") Hello pojo1, @RequestPart("pojo2") Hello pojo2,
@RequestPart("file") MultipartFile file);
@RequestMapping(method = RequestMethod.POST, path = "/multipartNames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipartNames", consumes = MULTIPART_FORM_DATA_VALUE, produces = 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)
@PostMapping(path = "/multipartFilenames", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String requestPartListOfMultipartFilesReturnsFileNames(@RequestPart("files") List<MultipartFile> files);
@RequestMapping(method = RequestMethod.POST, path = "/multipartPojosFiles",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipartPojosFiles", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String requestPartListOfPojosAndListOfMultipartFiles(@RequestPart("pojos") List<Hello> pojos,
@RequestPart("files") List<MultipartFile> files);
@RequestMapping(method = RequestMethod.POST, path = "/multipartNames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipartNames", consumes = MULTIPART_FORM_DATA_VALUE, produces = 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)
@PostMapping(path = "/multipartNames", consumes = MULTIPART_FORM_DATA_VALUE, produces = 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)
@PostMapping(path = "/multipartNames", consumes = MULTIPART_FORM_DATA_VALUE, produces = 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)
@PostMapping(path = "/invalid", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
produces = TEXT_PLAIN_VALUE)
String invalid(@RequestBody MultipartFile file);
}
@@ -435,36 +435,36 @@ class ValidFeignClientTests {
@FeignClient(name = "localapp", configuration = TestClientConfig.class)
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, path = "/hello")
@GetMapping("/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, path = "/hello")
@GetMapping("/hello")
Optional<Hello> getOptionalHello();
@RequestMapping(method = RequestMethod.GET, path = "${feignClient.methodLevelRequestMappingPath}")
@GetMapping("${feignClient.methodLevelRequestMappingPath}")
Hello getHelloUsingPropertyPlaceHolder();
@RequestMapping(method = RequestMethod.GET, path = "/hellos")
@GetMapping("/hellos")
List<Hello> getHellos();
@RequestMapping(method = RequestMethod.GET, path = "/hellostrings")
@GetMapping("/hellostrings")
List<String> getHelloStrings();
@RequestMapping(method = RequestMethod.GET, path = "/helloheaders")
@GetMapping("/helloheaders")
List<String> getHelloHeaders();
@RequestMapping(method = RequestMethod.GET, path = "/helloheadersplaceholders",
@GetMapping(path = "/helloheadersplaceholders",
headers = "myPlaceholderHeader=${feignClient.myPlaceholderHeader}")
String getHelloHeadersPlaceholders();
@RequestMapping(method = RequestMethod.GET, path = "/helloparams")
@GetMapping("/helloparams")
List<String> getParams(@RequestParam("params") List<String> params);
@RequestMapping(method = RequestMethod.GET, path = "/formattedparams")
@GetMapping("/formattedparams")
List<LocalDate> getFormattedParams(
@RequestParam("params") @DateTimeFormat(pattern = "dd-MM-yyyy") List<LocalDate> params);
@RequestMapping(method = RequestMethod.GET, path = "/noContent")
@GetMapping("/noContent")
ResponseEntity<Void> noContent();
@RequestMapping(method = RequestMethod.HEAD, path = "/head")
@@ -477,21 +477,24 @@ class ValidFeignClientTests {
produces = "application/vnd.io.spring.cloud.test.v1+json", path = "/complex")
String moreComplexContentType(String body);
@RequestMapping(method = RequestMethod.GET, path = "/tostring")
@GetMapping("/tostring")
String getToString(@RequestParam("arg") Arg arg);
@RequestMapping(method = RequestMethod.GET, path = "/tostring2")
@GetMapping("/tostring2")
String getToString(@RequestParam("arg") OtherArg arg);
@RequestMapping(method = RequestMethod.GET, path = "/tostringcollection")
@GetMapping("/tostringcollection")
Collection<String> getToString(@RequestParam("arg") Collection<OtherArg> args);
@PostMapping(path = "/form-urlencoded", consumes = APPLICATION_FORM_URLENCODED_VALUE)
Hello postFormUrlEncoded(Hello hello);
}
@FeignClient(name = "localapp1")
protected interface TestClientServiceId {
@RequestMapping(method = RequestMethod.GET, path = "/hello")
@GetMapping("/hello")
Hello getHello();
}
@@ -499,10 +502,10 @@ class ValidFeignClientTests {
@FeignClient(name = "localapp2", decode404 = true)
protected interface DecodingTestClient {
@RequestMapping(method = RequestMethod.GET, path = "/notFound")
@GetMapping("/notFound")
ResponseEntity<String> notFound();
@RequestMapping(method = RequestMethod.GET, path = "/notFound")
@GetMapping("/notFound")
Optional<String> optional();
}
@@ -526,22 +529,12 @@ class ValidFeignClientTests {
@Bean
public RequestInterceptor interceptor1() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
template.header(MYHEADER1, "myheader1value");
}
};
return template -> template.header(MYHEADER1, "myheader1value");
}
@Bean
public RequestInterceptor interceptor2() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
template.header(MYHEADER2, "myheader2value");
}
};
return template -> template.header(MYHEADER2, "myheader2value");
}
}
@@ -570,27 +563,21 @@ class ValidFeignClientTests {
@Bean
FeignFormatterRegistrar feignFormatterRegistrar() {
return new FeignFormatterRegistrar() {
return registry -> registry.addFormatter(new Formatter<OtherArg>() {
@Override
public void registerFormatters(FormatterRegistry registry) {
registry.addFormatter(new Formatter<OtherArg>() {
@Override
public String print(OtherArg object, Locale locale) {
if ("foo".equals(object.value)) {
return "bar";
}
return object.value;
}
@Override
public OtherArg parse(String text, Locale locale) throws ParseException {
return new OtherArg(text);
}
});
public String print(OtherArg object, Locale locale) {
if ("foo".equals(object.value)) {
return "bar";
}
return object.value;
}
};
@Override
public OtherArg parse(String text, Locale locale) throws ParseException {
return new OtherArg(text);
}
});
}
@Bean
@@ -598,23 +585,23 @@ class ValidFeignClientTests {
return new JsonFormWriter();
}
@RequestMapping(method = RequestMethod.GET, path = "/hello")
@GetMapping("/hello")
public Hello getHello() {
return new Hello(HELLO_WORLD_1);
}
@RequestMapping(method = RequestMethod.GET, path = "/hello2")
@GetMapping("/hello2")
public Hello getHello2() {
return new Hello(OI_TERRA_2);
}
@RequestMapping(method = RequestMethod.GET, path = "/hellos")
@GetMapping("/hellos")
public List<Hello> getHellos() {
ArrayList<Hello> hellos = getHelloList();
return hellos;
}
@RequestMapping(method = RequestMethod.GET, path = "/hellostrings")
@GetMapping("/hellostrings")
public List<String> getHelloStrings() {
ArrayList<String> hellos = new ArrayList<>();
hellos.add(HELLO_WORLD_1);
@@ -622,7 +609,7 @@ class ValidFeignClientTests {
return hellos;
}
@RequestMapping(method = RequestMethod.GET, path = "/helloheaders")
@GetMapping("/helloheaders")
public List<String> getHelloHeaders(@RequestHeader(MYHEADER1) String myheader1,
@RequestHeader(MYHEADER2) String myheader2) {
ArrayList<String> headers = new ArrayList<>();
@@ -631,23 +618,23 @@ class ValidFeignClientTests {
return headers;
}
@RequestMapping(method = RequestMethod.GET, path = "/helloheadersplaceholders")
@GetMapping("/helloheadersplaceholders")
public String getHelloHeadersPlaceholders(@RequestHeader("myPlaceholderHeader") String myPlaceholderHeader) {
return myPlaceholderHeader;
}
@RequestMapping(method = RequestMethod.GET, path = "/helloparams")
@GetMapping("/helloparams")
public List<String> getParams(@RequestParam("params") List<String> params) {
return params;
}
@RequestMapping(method = RequestMethod.GET, path = "/formattedparams")
@GetMapping("/formattedparams")
public List<LocalDate> getFormattedParams(
@RequestParam("params") @DateTimeFormat(pattern = "dd-MM-yyyy") List<LocalDate> params) {
return params;
}
@RequestMapping(method = RequestMethod.GET, path = "/noContent")
@GetMapping("/noContent")
ResponseEntity<Void> noContent() {
return ResponseEntity.noContent().build();
}
@@ -662,7 +649,7 @@ class ValidFeignClientTests {
throw new RuntimeException("always fails");
}
@RequestMapping(method = RequestMethod.GET, path = "/notFound")
@GetMapping("/notFound")
ResponseEntity<String> notFound() {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body((String) null);
}
@@ -676,17 +663,17 @@ class ValidFeignClientTests {
return body;
}
@RequestMapping(method = RequestMethod.GET, path = "/tostring")
@GetMapping("/tostring")
String getToString(@RequestParam("arg") Arg arg) {
return arg.toString();
}
@RequestMapping(method = RequestMethod.GET, path = "/tostring2")
@GetMapping("/tostring2")
String getToString(@RequestParam("arg") OtherArg arg) {
return arg.value;
}
@RequestMapping(method = RequestMethod.GET, path = "/tostringcollection")
@GetMapping("/tostringcollection")
Collection<String> getToString(@RequestParam("arg") Collection<OtherArg> args) {
List<String> result = new ArrayList<>();
for (OtherArg arg : args) {
@@ -695,47 +682,40 @@ class ValidFeignClientTests {
return result;
}
@RequestMapping(method = RequestMethod.POST, path = "/singlePart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/singlePart", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String singlePart(@RequestPart("hello") String hello) {
return hello;
}
@RequestMapping(method = RequestMethod.POST, path = "/singlePojoPart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/singlePojoPart", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String singlePojoPart(@RequestPart("hello") Hello hello) {
return hello.getMessage();
}
@RequestMapping(method = RequestMethod.POST, path = "/multipart",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipart", consumes = MULTIPART_FORM_DATA_VALUE, produces = 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 = "/multipartPojo",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipartPojo", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String multipartPojo(@RequestPart("hello") String hello, @RequestPart("world") String world,
@RequestPart("pojo1") Hello pojo1, @RequestPart("pojo2") Hello pojo2,
@RequestPart("file") MultipartFile file) {
return hello + world + pojo1.getMessage() + pojo2.getMessage() + file.getOriginalFilename();
}
@RequestMapping(method = RequestMethod.POST, path = "/multipartNames",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipartNames", consumes = MULTIPART_FORM_DATA_VALUE, produces = 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)
@PostMapping(path = "/multipartFilenames", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String multipartFilenames(HttpServletRequest request) throws Exception {
return request.getParts().stream().map(Part::getSubmittedFileName).collect(Collectors.joining(","));
}
@RequestMapping(method = RequestMethod.POST, path = "/multipartPojosFiles",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
@PostMapping(path = "/multipartPojosFiles", consumes = MULTIPART_FORM_DATA_VALUE, produces = TEXT_PLAIN_VALUE)
String requestPartListOfPojosAndListOfMultipartFiles(@RequestPart("pojos") List<Hello> pojos,
@RequestPart("files") List<MultipartFile> files) {
StringBuilder result = new StringBuilder();
@@ -751,6 +731,11 @@ class ValidFeignClientTests {
return result.toString();
}
@PostMapping(path = "/form-urlencoded", consumes = APPLICATION_FORM_URLENCODED_VALUE)
Hello postFormUrlEncoded(Hello hello) {
return hello;
}
}
public static class Hello {

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>3.0.2</version>
<version>3.1.0-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>