Add Support to CompletableFuture as return type in http service method.
// gh-34748 Signed-off-by: Mengqi Xu <2663479778@qq.com>
This commit is contained in:
parent
12146c4a1b
commit
0c552b8652
|
@ -22,6 +22,7 @@ import java.time.Duration;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -63,6 +64,7 @@ import org.springframework.web.service.annotation.HttpExchange;
|
|||
* @author Sebastien Deleuze
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Sam Brannen
|
||||
* @author Mengqi Xu
|
||||
* @since 6.0
|
||||
*/
|
||||
final class HttpServiceMethod {
|
||||
|
@ -412,7 +414,29 @@ final class HttpServiceMethod {
|
|||
"Kotlin Coroutines are only supported with reactive implementations");
|
||||
}
|
||||
|
||||
MethodParameter param = new MethodParameter(method, -1).nestedIfOptional();
|
||||
MethodParameter param = new MethodParameter(method, -1);
|
||||
Class<?> paramType = param.getNestedParameterType();
|
||||
|
||||
Function<HttpRequestValues, @Nullable Object> responseFunction;
|
||||
if (paramType.equals(CompletableFuture.class)) {
|
||||
MethodParameter bodyParam = param.nested();
|
||||
MethodParameter nestedParamIfOptional = bodyParam.getNestedParameterType().equals(Optional.class) ?
|
||||
bodyParam.nested() : bodyParam;
|
||||
responseFunction = request ->
|
||||
CompletableFuture.supplyAsync(() ->
|
||||
asOptionalIfNecessary(buildResponseFunction(client, nestedParamIfOptional).apply(request),
|
||||
bodyParam.getNestedParameterType()));
|
||||
}
|
||||
else {
|
||||
responseFunction = request ->
|
||||
asOptionalIfNecessary(buildResponseFunction(client, param.nestedIfOptional()).apply(request),
|
||||
param.getParameterType());
|
||||
}
|
||||
|
||||
return new ExchangeResponseFunction(responseFunction);
|
||||
}
|
||||
|
||||
private static Function<HttpRequestValues, @Nullable Object> buildResponseFunction(HttpExchangeAdapter client, MethodParameter param) {
|
||||
Class<?> paramType = param.getNestedParameterType();
|
||||
|
||||
Function<HttpRequestValues, @Nullable Object> responseFunction;
|
||||
|
@ -423,33 +447,30 @@ final class HttpServiceMethod {
|
|||
};
|
||||
}
|
||||
else if (paramType.equals(HttpHeaders.class)) {
|
||||
responseFunction = request -> asOptionalIfNecessary(client.exchangeForHeaders(request), param);
|
||||
responseFunction = client::exchangeForHeaders;
|
||||
}
|
||||
else if (paramType.equals(ResponseEntity.class)) {
|
||||
MethodParameter bodyParam = param.nested();
|
||||
if (bodyParam.getNestedParameterType().equals(Void.class)) {
|
||||
responseFunction = request ->
|
||||
asOptionalIfNecessary(client.exchangeForBodilessEntity(request), param);
|
||||
responseFunction = client::exchangeForBodilessEntity;
|
||||
}
|
||||
else {
|
||||
ParameterizedTypeReference<?> bodyTypeRef =
|
||||
ParameterizedTypeReference.forType(bodyParam.getNestedGenericParameterType());
|
||||
responseFunction = request ->
|
||||
asOptionalIfNecessary(client.exchangeForEntity(request, bodyTypeRef), param);
|
||||
responseFunction = request -> client.exchangeForEntity(request, bodyTypeRef);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ParameterizedTypeReference<?> bodyTypeRef =
|
||||
ParameterizedTypeReference.forType(param.getNestedGenericParameterType());
|
||||
responseFunction = request ->
|
||||
asOptionalIfNecessary(client.exchangeForBody(request, bodyTypeRef), param);
|
||||
responseFunction = request -> client.exchangeForBody(request, bodyTypeRef);
|
||||
}
|
||||
|
||||
return new ExchangeResponseFunction(responseFunction);
|
||||
return responseFunction;
|
||||
}
|
||||
|
||||
private static @Nullable Object asOptionalIfNecessary(@Nullable Object response, MethodParameter param) {
|
||||
return param.getParameterType().equals(Optional.class) ? Optional.ofNullable(response) : response;
|
||||
private static @Nullable Object asOptionalIfNecessary(@Nullable Object response, Class<?> type) {
|
||||
return type.equals(Optional.class) ? Optional.ofNullable(response) : response;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.lang.annotation.Target;
|
|||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
@ -61,6 +63,7 @@ import static org.springframework.http.MediaType.APPLICATION_NDJSON_VALUE;
|
|||
* @author Rossen Stoyanchev
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Sam Brannen
|
||||
* @author Mengqi Xu
|
||||
*/
|
||||
class HttpServiceMethodTests {
|
||||
|
||||
|
@ -103,6 +106,34 @@ class HttpServiceMethodTests {
|
|||
assertThat(list).containsOnly("exchangeForBody");
|
||||
}
|
||||
|
||||
@Test // gh-34748
|
||||
void completableFutureService() throws ExecutionException, InterruptedException {
|
||||
CompletableFutureService service = this.proxyFactory.createClient(CompletableFutureService.class);
|
||||
|
||||
service.execute();
|
||||
|
||||
HttpHeaders headers = service.getHeaders().get();
|
||||
assertThat(headers).isNotNull();
|
||||
|
||||
String body = service.getBody().get();
|
||||
assertThat(body).isEqualTo(this.client.getInvokedMethodName());
|
||||
|
||||
Optional<String> optional = service.getBodyOptional().get();
|
||||
assertThat(optional.get()).isEqualTo("exchangeForBody");
|
||||
|
||||
ResponseEntity<String> entity = service.getEntity().get();
|
||||
assertThat(entity.getBody()).isEqualTo("exchangeForEntity");
|
||||
|
||||
Optional<ResponseEntity<String>> entityOptional = service.getEntityOptional().get();
|
||||
assertThat(entityOptional.get().getBody()).isEqualTo("exchangeForEntity");
|
||||
|
||||
ResponseEntity<Void> voidEntity = service.getVoidEntity().get();
|
||||
assertThat(voidEntity.getBody()).isNull();
|
||||
|
||||
List<String> list = service.getList().get();
|
||||
assertThat(list).containsOnly("exchangeForBody");
|
||||
}
|
||||
|
||||
@Test
|
||||
void reactorService() {
|
||||
ReactorService service = this.reactorProxyFactory.createClient(ReactorService.class);
|
||||
|
@ -294,6 +325,35 @@ class HttpServiceMethodTests {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private interface CompletableFutureService {
|
||||
|
||||
@GetExchange
|
||||
CompletableFuture<Void> execute();
|
||||
|
||||
@GetExchange
|
||||
CompletableFuture<HttpHeaders> getHeaders();
|
||||
|
||||
@GetExchange
|
||||
CompletableFuture<String> getBody();
|
||||
|
||||
@GetExchange
|
||||
CompletableFuture<Optional<String>> getBodyOptional();
|
||||
|
||||
@GetExchange
|
||||
CompletableFuture<ResponseEntity<Void>> getVoidEntity();
|
||||
|
||||
@GetExchange
|
||||
CompletableFuture<ResponseEntity<String>> getEntity();
|
||||
|
||||
@GetExchange
|
||||
CompletableFuture<Optional<ResponseEntity<String>>> getEntityOptional();
|
||||
|
||||
@GetExchange
|
||||
CompletableFuture<List<String>> getList();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private interface ReactorService {
|
||||
|
||||
|
|
Loading…
Reference in New Issue