Merge remote-tracking branch 'origin/main' into 3.1.x
This commit is contained in:
5
.github/workflows/maven.yml
vendored
5
.github/workflows/maven.yml
vendored
@@ -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
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user