Skip to content

Commit 307247b

Browse files
committed
Use DispatchExceptionHandler in HandlerResult
Commit #2878ad added the DispatchExceptionHandler contract for mapping an error before a handler is selected to a HandlerResult. The same is also convenient for use in HandlerResult itself which currently uses a java.util.Function essentially for the same. See gh-22991
1 parent a9a7868 commit 307247b

File tree

4 files changed

+61
-24
lines changed

4 files changed

+61
-24
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java

+15-10
Original file line numberDiff line numberDiff line change
@@ -189,24 +189,29 @@ private Mono<Void> handleRequestWith(ServerWebExchange exchange, Object handler)
189189
}
190190

191191
private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
192-
return getResultHandler(result).handleResult(exchange, result)
193-
.checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]")
194-
.onErrorResume(ex ->
195-
result.applyExceptionHandler(ex).flatMap(exResult ->
196-
getResultHandler(exResult).handleResult(exchange, exResult)
197-
.checkpoint("Exception handler " + exResult.getHandler() + ", " +
198-
"error=\"" + ex.getMessage() + "\" [DispatcherHandler]")));
192+
Mono<Void> resultMono = doHandleResult(exchange, result, "Handler " + result.getHandler());
193+
if (result.getExceptionHandler() != null) {
194+
resultMono = resultMono.onErrorResume(ex ->
195+
result.getExceptionHandler().handleError(exchange, ex).flatMap(result2 ->
196+
doHandleResult(exchange, result2, "Exception handler " +
197+
result2.getHandler() + ", error=\"" + ex.getMessage() + "\"")));
198+
}
199+
return resultMono;
199200
}
200201

201-
private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
202+
private Mono<Void> doHandleResult(
203+
ServerWebExchange exchange, HandlerResult handlerResult, String description) {
204+
202205
if (this.resultHandlers != null) {
203206
for (HandlerResultHandler resultHandler : this.resultHandlers) {
204207
if (resultHandler.supports(handlerResult)) {
205-
return resultHandler;
208+
description += " [DispatcherHandler]";
209+
return resultHandler.handleResult(exchange, handlerResult).checkpoint(description);
206210
}
207211
}
208212
}
209-
throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
213+
return Mono.error(new IllegalStateException(
214+
"No HandlerResultHandler for " + handlerResult.getReturnValue()));
210215
}
211216

212217
@Override

spring-webflux/src/main/java/org/springframework/web/reactive/HandlerAdapter.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.web.reactive;
1818

19-
import java.util.function.Function;
20-
2119
import reactor.core.publisher.Mono;
2220

2321
import org.springframework.web.server.ServerWebExchange;
@@ -27,7 +25,7 @@
2725
* invoking a handler and makes it possible to support any handler type.
2826
*
2927
* <p>A {@code HandlerAdapter} can implement {@link DispatchExceptionHandler}
30-
* if it wants to handle an exception that occured before the request is mapped
28+
* if it wants to handle an exception that occurred before the request is mapped
3129
* to a handler. This allows the {@code HandlerAdapter} to expose a consistent
3230
* exception handling mechanism for any request handling error.
3331
* In Reactive Streams terms, {@link #handle} processes the onNext, while
@@ -54,9 +52,9 @@ public interface HandlerAdapter {
5452
* result that represents an error response.
5553
* <p>Furthermore since an async {@code HandlerResult} may produce an error
5654
* later during result handling implementations are also encouraged to
57-
* {@link HandlerResult#setExceptionHandler(Function) set an exception
58-
* handler} on the {@code HandlerResult} so that may also be applied later
59-
* after result handling.
55+
* {@link HandlerResult#setExceptionHandler(DispatchExceptionHandler) set
56+
* an exception handler} on the {@code HandlerResult} so that may also be
57+
* applied later after result handling.
6058
* @param exchange current server exchange
6159
* @param handler the selected handler which must have been previously
6260
* checked via {@link #supports(Object)}

spring-webflux/src/main/java/org/springframework/web/reactive/HandlerResult.java

+39-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -44,7 +44,10 @@ public class HandlerResult {
4444
private final BindingContext bindingContext;
4545

4646
@Nullable
47-
private Function<Throwable, Mono<HandlerResult>> exceptionHandler;
47+
private DispatchExceptionHandler exceptionHandler;
48+
49+
@Nullable
50+
private Function<Throwable, Mono<HandlerResult>> exceptionHandlerFunction;
4851

4952

5053
/**
@@ -124,21 +127,50 @@ public Model getModel() {
124127
return this.bindingContext.getModel();
125128
}
126129

130+
/**
131+
* A {@link HandlerAdapter} may use this to provide an exception handler
132+
* to use to map exceptions from handling this result into an alternative
133+
* one. Especially when the return value is asynchronous, an exception is
134+
* not be produced at the point of handler invocation, but rather later when
135+
* result handling causes the actual value or an exception to be produced.
136+
* @param exceptionHandler the exception handler to use
137+
* @since 6.0
138+
*/
139+
public HandlerResult setExceptionHandler(DispatchExceptionHandler exceptionHandler) {
140+
this.exceptionHandler = exceptionHandler;
141+
return this;
142+
}
143+
144+
/**
145+
* Return the {@link #setExceptionHandler(DispatchExceptionHandler)
146+
* configured} exception handler.
147+
* @since 6.0
148+
*/
149+
@Nullable
150+
public DispatchExceptionHandler getExceptionHandler() {
151+
return this.exceptionHandler;
152+
}
153+
127154
/**
128155
* Configure an exception handler that may be used to produce an alternative
129156
* result when result handling fails. Especially for an async return value
130157
* errors may occur after the invocation of the handler.
131158
* @param function the error handler
132159
* @return the current instance
160+
* @deprecated in favor of {@link #setExceptionHandler(DispatchExceptionHandler)}
133161
*/
162+
@Deprecated(since = "6.0", forRemoval = true)
134163
public HandlerResult setExceptionHandler(Function<Throwable, Mono<HandlerResult>> function) {
135-
this.exceptionHandler = function;
164+
this.exceptionHandler = (exchange, ex) -> function.apply(ex);
165+
this.exceptionHandlerFunction = function;
136166
return this;
137167
}
138168

139169
/**
140170
* Whether there is an exception handler.
171+
* @deprecated in favor of checking via {@link #getExceptionHandler()}
141172
*/
173+
@Deprecated(since = "6.0", forRemoval = true)
142174
public boolean hasExceptionHandler() {
143175
return (this.exceptionHandler != null);
144176
}
@@ -147,9 +179,12 @@ public boolean hasExceptionHandler() {
147179
* Apply the exception handler and return the alternative result.
148180
* @param failure the exception
149181
* @return the new result or the same error if there is no exception handler
182+
* @deprecated without a replacement; for internal invocation only, not used as of 6.0
150183
*/
184+
@Deprecated(since = "6.0", forRemoval = true)
151185
public Mono<HandlerResult> applyExceptionHandler(Throwable failure) {
152-
return (this.exceptionHandler != null ? this.exceptionHandler.apply(failure) : Mono.error(failure));
186+
return (this.exceptionHandlerFunction != null ?
187+
this.exceptionHandlerFunction.apply(failure) : Mono.error(failure));
153188
}
154189

155190
}

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.ArrayList;
2020
import java.util.Collections;
2121
import java.util.List;
22-
import java.util.function.Function;
2322

2423
import org.apache.commons.logging.Log;
2524
import org.apache.commons.logging.LogFactory;
@@ -194,15 +193,15 @@ public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
194193

195194
InvocableHandlerMethod invocableMethod = this.methodResolver.getRequestMappingMethod(handlerMethod);
196195

197-
Function<Throwable, Mono<HandlerResult>> exceptionHandler =
198-
ex -> handleException(exchange, ex, handlerMethod, bindingContext);
196+
DispatchExceptionHandler exceptionHandler =
197+
(exchange2, ex) -> handleException(exchange, ex, handlerMethod, bindingContext);
199198

200199
return this.modelInitializer
201200
.initModel(handlerMethod, bindingContext, exchange)
202201
.then(Mono.defer(() -> invocableMethod.invoke(exchange, bindingContext)))
203202
.doOnNext(result -> result.setExceptionHandler(exceptionHandler))
204203
.doOnNext(result -> bindingContext.saveModel())
205-
.onErrorResume(exceptionHandler);
204+
.onErrorResume(ex -> exceptionHandler.handleError(exchange, ex));
206205
}
207206

208207
private Mono<HandlerResult> handleException(

0 commit comments

Comments
 (0)