Browse Source

Optimize WebClientUtils

Use constant Predicate for exception wrapping.
Use ResponseEntity constructor instead of builder.

See gh-26069
pull/26087/head
Rossen Stoyanchev 4 years ago
parent
commit
ba9325446c
  1. 15
      spring-web/src/main/java/org/springframework/http/ResponseEntity.java
  2. 30
      spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java
  3. 2
      spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java
  4. 27
      spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientUtils.java

15
spring-web/src/main/java/org/springframework/http/ResponseEntity.java

@ -113,9 +113,18 @@ public class ResponseEntity<T> extends HttpEntity<T> {
* @param status the status code * @param status the status code
*/ */
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) { public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {
super(body, headers); this(body, headers, (Object) status);
Assert.notNull(status, "HttpStatus must not be null"); }
this.status = status;
/**
* Create a new {@code HttpEntity} with the given body, headers, and status code.
* @param body the entity body
* @param headers the entity headers
* @param rawStatus the status code value
* @since 5.3.2
*/
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, int rawStatus) {
this(body, headers, (Object) rawStatus);
} }
/** /**

30
spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java

@ -543,16 +543,10 @@ class DefaultWebClient implements WebClient {
return this.responseMono.flatMap(response -> handleBodyMono(response, response.bodyToMono(elementTypeRef))); return this.responseMono.flatMap(response -> handleBodyMono(response, response.bodyToMono(elementTypeRef)));
} }
private <T> Mono<T> handleBodyMono(ClientResponse response, Mono<T> bodyPublisher) { private <T> Mono<T> handleBodyMono(ClientResponse response, Mono<T> body) {
body = body.onErrorResume(WebClientUtils.WRAP_EXCEPTION_PREDICATE, exceptionWrappingFunction(response));
Mono<T> result = statusHandlers(response); Mono<T> result = statusHandlers(response);
Mono<T> wrappedExceptions = bodyPublisher.onErrorResume(WebClientUtils::shouldWrapException, return (result != null ? result.switchIfEmpty(body) : body);
t -> wrapException(t, response));
if (result != null) {
return result.switchIfEmpty(wrappedExceptions);
}
else {
return wrappedExceptions;
}
} }
@Override @Override
@ -567,16 +561,10 @@ class DefaultWebClient implements WebClient {
return this.responseMono.flatMapMany(response -> handleBodyFlux(response, response.bodyToFlux(elementTypeRef))); return this.responseMono.flatMapMany(response -> handleBodyFlux(response, response.bodyToFlux(elementTypeRef)));
} }
private <T> Publisher<T> handleBodyFlux(ClientResponse response, Flux<T> bodyPublisher) { private <T> Publisher<T> handleBodyFlux(ClientResponse response, Flux<T> body) {
body = body.onErrorResume(WebClientUtils.WRAP_EXCEPTION_PREDICATE, exceptionWrappingFunction(response));
Mono<T> result = statusHandlers(response); Mono<T> result = statusHandlers(response);
Flux<T> wrappedExceptions = bodyPublisher.onErrorResume(WebClientUtils::shouldWrapException, return (result != null ? result.flux().switchIfEmpty(body) : body);
t -> wrapException(t, response));
if (result != null) {
return result.flux().switchIfEmpty(wrappedExceptions);
}
else {
return wrappedExceptions;
}
} }
@Nullable @Nullable
@ -608,10 +596,8 @@ class DefaultWebClient implements WebClient {
return result.checkpoint(description); return result.checkpoint(description);
} }
private <T> Mono<T> wrapException(Throwable throwable, ClientResponse response) { private <T> Function<Throwable, Mono<? extends T>> exceptionWrappingFunction(ClientResponse response) {
return response.createException() return t -> response.createException().flatMap(ex -> Mono.error(ex.initCause(t)));
.map(responseException -> responseException.initCause(throwable))
.flatMap(Mono::error);
} }
@Override @Override

2
spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java

@ -104,7 +104,7 @@ public abstract class ExchangeFunctions {
.connect(httpMethod, url, httpRequest -> clientRequest.writeTo(httpRequest, this.strategies)) .connect(httpMethod, url, httpRequest -> clientRequest.writeTo(httpRequest, this.strategies))
.doOnRequest(n -> logRequest(clientRequest)) .doOnRequest(n -> logRequest(clientRequest))
.doOnCancel(() -> logger.debug(logPrefix + "Cancel signal (to close connection)")) .doOnCancel(() -> logger.debug(logPrefix + "Cancel signal (to close connection)"))
.onErrorResume(WebClientUtils::shouldWrapException, t -> wrapException(t, clientRequest)) .onErrorResume(WebClientUtils.WRAP_EXCEPTION_PREDICATE, t -> wrapException(t, clientRequest))
.map(httpResponse -> { .map(httpResponse -> {
logResponse(httpResponse, logPrefix); logResponse(httpResponse, logPrefix);
return new DefaultClientResponse( return new DefaultClientResponse(

27
spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientUtils.java

@ -17,6 +17,7 @@
package org.springframework.web.reactive.function.client; package org.springframework.web.reactive.function.client;
import java.util.List; import java.util.List;
import java.util.function.Predicate;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
@ -26,7 +27,8 @@ import org.springframework.core.codec.CodecException;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
/** /**
* Internal methods shared between {@link DefaultWebClient} and {@link DefaultClientResponse}. * Internal methods shared between {@link DefaultWebClient} and
* {@link DefaultClientResponse}.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 5.2 * @since 5.2
@ -35,6 +37,12 @@ abstract class WebClientUtils {
private static final String VALUE_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n"; private static final String VALUE_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n";
/**
* Predicate that returns true if an exception should be wrapped.
*/
public final static Predicate<? super Throwable> WRAP_EXCEPTION_PREDICATE =
t -> !(t instanceof WebClientException) && !(t instanceof CodecException);
/** /**
* Map the given response to a single value {@code ResponseEntity<T>}. * Map the given response to a single value {@code ResponseEntity<T>}.
@ -42,9 +50,10 @@ abstract class WebClientUtils {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> Mono<ResponseEntity<T>> mapToEntity(ClientResponse response, Mono<T> bodyMono) { public static <T> Mono<ResponseEntity<T>> mapToEntity(ClientResponse response, Mono<T> bodyMono) {
return ((Mono<Object>) bodyMono).defaultIfEmpty(VALUE_NONE).map(body -> return ((Mono<Object>) bodyMono).defaultIfEmpty(VALUE_NONE).map(body ->
ResponseEntity.status(response.rawStatusCode()) new ResponseEntity<>(
.headers(response.headers().asHttpHeaders()) body != VALUE_NONE ? (T) body : null,
.body(body != VALUE_NONE ? (T) body : null)); response.headers().asHttpHeaders(),
response.rawStatusCode()));
} }
/** /**
@ -52,15 +61,7 @@ abstract class WebClientUtils {
*/ */
public static <T> Mono<ResponseEntity<List<T>>> mapToEntityList(ClientResponse response, Publisher<T> body) { public static <T> Mono<ResponseEntity<List<T>>> mapToEntityList(ClientResponse response, Publisher<T> body) {
return Flux.from(body).collectList().map(list -> return Flux.from(body).collectList().map(list ->
ResponseEntity.status(response.rawStatusCode()) new ResponseEntity<>(list, response.headers().asHttpHeaders(), response.rawStatusCode()));
.headers(response.headers().asHttpHeaders())
.body(list));
} }
/**
* Indicates whether the given exception should be wrapped.
*/
public static boolean shouldWrapException(Throwable t) {
return !(t instanceof WebClientException) && !(t instanceof CodecException);
}
} }

Loading…
Cancel
Save