Skip to content

Commit 571f6ad

Browse files
committed
Merge branch '5.1.x'
2 parents 9648b1c + 6cb4b8b commit 571f6ad

File tree

3 files changed

+77
-28
lines changed

3 files changed

+77
-28
lines changed

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

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Map;
2727
import java.util.function.Consumer;
2828
import java.util.function.Function;
29+
import java.util.function.IntPredicate;
2930
import java.util.function.Predicate;
3031
import java.util.function.Supplier;
3132

@@ -416,6 +417,8 @@ public HttpHeaders getHeaders() {
416417

417418
private static class DefaultResponseSpec implements ResponseSpec {
418419

420+
private static final IntPredicate STATUS_CODE_ERROR = value -> value >= 400;
421+
419422
private final Mono<ClientResponse> responseMono;
420423

421424
private final Supplier<HttpRequest> requestSupplier;
@@ -425,17 +428,30 @@ private static class DefaultResponseSpec implements ResponseSpec {
425428
DefaultResponseSpec(Mono<ClientResponse> responseMono, Supplier<HttpRequest> requestSupplier) {
426429
this.responseMono = responseMono;
427430
this.requestSupplier = requestSupplier;
428-
this.statusHandlers.add(new StatusHandler(HttpStatus::isError, ClientResponse::createException));
431+
this.statusHandlers.add(new StatusHandler(STATUS_CODE_ERROR, ClientResponse::createException));
429432
}
430433

431434
@Override
432435
public ResponseSpec onStatus(Predicate<HttpStatus> statusPredicate,
433436
Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) {
437+
return onRawStatus(toIntPredicate(statusPredicate), exceptionFunction);
438+
}
434439

435-
Assert.notNull(statusPredicate, "StatusPredicate must not be null");
440+
private static IntPredicate toIntPredicate(Predicate<HttpStatus> predicate) {
441+
return value -> {
442+
HttpStatus status = HttpStatus.resolve(value);
443+
return (status != null) && predicate.test(status);
444+
};
445+
}
446+
447+
@Override
448+
public ResponseSpec onRawStatus(IntPredicate statusCodePredicate,
449+
Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) {
450+
451+
Assert.notNull(statusCodePredicate, "StatusCodePredicate must not be null");
436452
Assert.notNull(exceptionFunction, "Function must not be null");
437453

438-
this.statusHandlers.add(0, new StatusHandler(statusPredicate, exceptionFunction));
454+
this.statusHandlers.add(0, new StatusHandler(statusCodePredicate, exceptionFunction));
439455
return this;
440456
}
441457

@@ -452,17 +468,12 @@ public <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> elementTypeRef) {
452468
}
453469

454470
private <T> Mono<T> handleBodyMono(ClientResponse response, Mono<T> bodyPublisher) {
455-
if (HttpStatus.resolve(response.rawStatusCode()) != null) {
456-
Mono<T> result = statusHandlers(response);
457-
if (result != null) {
458-
return result.switchIfEmpty(bodyPublisher);
459-
}
460-
else {
461-
return bodyPublisher;
462-
}
471+
Mono<T> result = statusHandlers(response);
472+
if (result != null) {
473+
return result.switchIfEmpty(bodyPublisher);
463474
}
464475
else {
465-
return response.createException().flatMap(Mono::error);
476+
return bodyPublisher;
466477
}
467478
}
468479

@@ -479,24 +490,20 @@ public <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef) {
479490
}
480491

481492
private <T> Publisher<T> handleBodyFlux(ClientResponse response, Flux<T> bodyPublisher) {
482-
if (HttpStatus.resolve(response.rawStatusCode()) != null) {
483-
Mono<T> result = statusHandlers(response);
484-
if (result != null) {
485-
return result.flux().switchIfEmpty(bodyPublisher);
486-
}
487-
else {
488-
return bodyPublisher;
489-
}
493+
Mono<T> result = statusHandlers(response);
494+
if (result != null) {
495+
return result.flux().switchIfEmpty(bodyPublisher);
490496
}
491497
else {
492-
return response.createException().flatMap(Mono::error);
498+
return bodyPublisher;
493499
}
494500
}
495501

496502
@Nullable
497503
private <T> Mono<T> statusHandlers(ClientResponse response) {
504+
int statusCode = response.rawStatusCode();
498505
for (StatusHandler handler : this.statusHandlers) {
499-
if (handler.test(response.statusCode())) {
506+
if (handler.test(statusCode)) {
500507
Mono<? extends Throwable> exMono;
501508
try {
502509
exMono = handler.apply(response);
@@ -508,7 +515,7 @@ private <T> Mono<T> statusHandlers(ClientResponse response) {
508515
}
509516
Mono<T> result = exMono.flatMap(Mono::error);
510517
HttpRequest request = this.requestSupplier.get();
511-
return insertCheckpoint(result, response.statusCode(), request);
518+
return insertCheckpoint(result, statusCode, request);
512519
}
513520
}
514521
return null;
@@ -522,10 +529,10 @@ private <T> Mono<T> drainBody(ClientResponse response, Throwable ex) {
522529
.onErrorResume(ex2 -> Mono.empty()).thenReturn(ex);
523530
}
524531

525-
private <T> Mono<T> insertCheckpoint(Mono<T> result, HttpStatus status, HttpRequest request) {
532+
private <T> Mono<T> insertCheckpoint(Mono<T> result, int statusCode, HttpRequest request) {
526533
String httpMethod = request.getMethodValue();
527534
URI uri = request.getURI();
528-
String description = status + " from " + httpMethod + " " + uri + " [DefaultWebClient]";
535+
String description = statusCode + " from " + httpMethod + " " + uri + " [DefaultWebClient]";
529536
return result.checkpoint(description);
530537
}
531538

@@ -558,24 +565,25 @@ public <T> Mono<ResponseEntity<List<T>>> toEntityList(ParameterizedTypeReference
558565

559566
private static class StatusHandler {
560567

561-
private final Predicate<HttpStatus> predicate;
568+
private final IntPredicate predicate;
562569

563570
private final Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction;
564571

565-
public StatusHandler(Predicate<HttpStatus> predicate,
572+
public StatusHandler(IntPredicate predicate,
566573
Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) {
567574

568575
this.predicate = predicate;
569576
this.exceptionFunction = exceptionFunction;
570577
}
571578

572-
public boolean test(HttpStatus status) {
579+
public boolean test(int status) {
573580
return this.predicate.test(status);
574581
}
575582

576583
public Mono<? extends Throwable> apply(ClientResponse response) {
577584
return this.exceptionFunction.apply(response);
578585
}
586+
579587
}
580588
}
581589

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Map;
2424
import java.util.function.Consumer;
2525
import java.util.function.Function;
26+
import java.util.function.IntPredicate;
2627
import java.util.function.Predicate;
2728

2829
import org.reactivestreams.Publisher;
@@ -691,6 +692,24 @@ interface ResponseSpec {
691692
ResponseSpec onStatus(Predicate<HttpStatus> statusPredicate,
692693
Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction);
693694

695+
/**
696+
* Register a custom error function that gets invoked when the given raw status code
697+
* predicate applies. The exception returned from the function will be returned from
698+
* {@link #bodyToMono(Class)} and {@link #bodyToFlux(Class)}.
699+
* <p>By default, an error handler is registered that throws a
700+
* {@link WebClientResponseException} when the response status code is 4xx or 5xx.
701+
* @param statusCodePredicate a predicate of the raw status code that indicates
702+
* whether {@code exceptionFunction} applies.
703+
* <p><strong>NOTE:</strong> if the response is expected to have content,
704+
* the exceptionFunction should consume it. If not, the content will be
705+
* automatically drained to ensure resources are released.
706+
* @param exceptionFunction the function that returns the exception
707+
* @return this builder
708+
* @since 5.1.9
709+
*/
710+
ResponseSpec onRawStatus(IntPredicate statusCodePredicate,
711+
Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction);
712+
694713
/**
695714
* Extract the body to a {@code Mono}. By default, if the response has status code 4xx or
696715
* 5xx, the {@code Mono} will contain a {@link WebClientException}. This can be overridden

spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,28 @@ public void shouldApplyCustomStatusHandler() {
730730
});
731731
}
732732

733+
@Test
734+
public void shouldApplyCustomRawStatusHandler() {
735+
prepareResponse(response -> response.setResponseCode(500)
736+
.setHeader("Content-Type", "text/plain").setBody("Internal Server error"));
737+
738+
Mono<String> result = this.webClient.get()
739+
.uri("/greeting?name=Spring")
740+
.retrieve()
741+
.onRawStatus(value -> value >= 500 && value < 600, response -> Mono.just(new MyException("500 error!")))
742+
.bodyToMono(String.class);
743+
744+
StepVerifier.create(result)
745+
.expectError(MyException.class)
746+
.verify(Duration.ofSeconds(3));
747+
748+
expectRequestCount(1);
749+
expectRequest(request -> {
750+
assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo("*/*");
751+
assertThat(request.getPath()).isEqualTo("/greeting?name=Spring");
752+
});
753+
}
754+
733755
@Test
734756
public void shouldApplyCustomStatusHandlerParameterizedTypeReference() {
735757
prepareResponse(response -> response.setResponseCode(500)

0 commit comments

Comments
 (0)