Refine exception handling
Refine exception handling responsibilities between the DispatcherHandler and the HandlerAdapter.
This commit is contained in:
@@ -117,35 +117,29 @@ public class DispatcherHandler implements HttpHandler, ApplicationContextAware {
|
||||
logger.debug("Processing " + request.getMethod() + " request for [" + request.getURI() + "]");
|
||||
}
|
||||
return Flux.fromIterable(this.handlerMappings)
|
||||
.concatMap(m -> m.getHandler(request))
|
||||
.concatMap(mapping -> mapping.getHandler(request))
|
||||
.next()
|
||||
.then(handler -> getHandlerAdapter(handler).handle(request, response, handler))
|
||||
.then(result -> {
|
||||
Mono<Void> mono = (result.hasError() ? Mono.error(result.getError()) :
|
||||
handleResult(request, response, result));
|
||||
if (result.hasExceptionMapper()) {
|
||||
return mono.otherwise(ex -> result.getExceptionMapper().apply(ex)
|
||||
.then(exResult -> handleResult(request, response, exResult)));
|
||||
}
|
||||
return mono;
|
||||
})
|
||||
.then(handler -> invokeHandler(request, response, handler))
|
||||
.then(result -> handleResult(request, response, result))
|
||||
.otherwise(ex -> Mono.error(this.errorMapper.apply(ex)));
|
||||
}
|
||||
|
||||
protected HandlerAdapter getHandlerAdapter(Object handler) {
|
||||
private Mono<HandlerResult> invokeHandler(ServerHttpRequest request, ServerHttpResponse response, Object handler) {
|
||||
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
|
||||
if (handlerAdapter.supports(handler)) {
|
||||
return handlerAdapter;
|
||||
return handlerAdapter.handle(request, response, handler);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No HandlerAdapter: " + handler);
|
||||
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
|
||||
}
|
||||
|
||||
protected Mono<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response, HandlerResult result) {
|
||||
return getResultHandler(result).handleResult(request, response, result);
|
||||
private Mono<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response, HandlerResult result) {
|
||||
return getResultHandler(result).handleResult(request, response, result)
|
||||
.otherwise(ex -> result.applyExceptionHandler(ex).then(exceptionResult ->
|
||||
getResultHandler(result).handleResult(request, response, exceptionResult)));
|
||||
}
|
||||
|
||||
protected HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
|
||||
private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
|
||||
for (HandlerResultHandler resultHandler : resultHandlers) {
|
||||
if (resultHandler.supports(handlerResult)) {
|
||||
return resultHandler;
|
||||
|
||||
@@ -16,17 +16,16 @@
|
||||
|
||||
package org.springframework.web.reactive;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.Mono;
|
||||
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
|
||||
/**
|
||||
* Interface that must be implemented for each handler type to handle an HTTP request.
|
||||
* This interface is used to allow the {@link DispatcherHandler} to be indefinitely
|
||||
* extensible. The {@code DispatcherHandler} accesses all installed handlers through
|
||||
* this interface, meaning that it does not contain code specific to any handler type.
|
||||
* Contract that decouples the {@link DispatcherHandler} from the details of
|
||||
* invoking a handler and makes it possible to support any handler type.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
@@ -34,26 +33,32 @@ import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
public interface HandlerAdapter {
|
||||
|
||||
/**
|
||||
* Given a handler instance, return whether or not this {@code HandlerAdapter}
|
||||
* can support it. Typical HandlerAdapters will base the decision on the handler
|
||||
* type. HandlerAdapters will usually only support one handler type each.
|
||||
* <p>A typical implementation:
|
||||
* <p>{@code
|
||||
* return (handler instanceof MyHandler);
|
||||
* }
|
||||
* Whether this {@code HandlerAdapter} supports the given {@code handler}.
|
||||
*
|
||||
* @param handler handler object to check
|
||||
* @return whether or not this object can use the given handler
|
||||
* @return whether or not the handler is supported
|
||||
*/
|
||||
boolean supports(Object handler);
|
||||
|
||||
/**
|
||||
* Use the given handler to handle this request.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler handler to use. This object must have previously been passed
|
||||
* to the {@code supports} method of this interface, which must have
|
||||
* returned {@code true}.
|
||||
* @return A {@link Mono} that emits a single {@link HandlerResult} element
|
||||
* Handle the request with the given handler.
|
||||
*
|
||||
* <p>Implementations are encouraged to handle exceptions resulting from the
|
||||
* invocation of a handler in order and if necessary to return an alternate
|
||||
* result that represents an error response.
|
||||
*
|
||||
* <p>Furthermore since an async {@code HandlerResult} may produce an error
|
||||
* later during result handling implementations are also encouraged to
|
||||
* {@link HandlerResult#setExceptionHandler(Function) set an exception
|
||||
* handler} on the {@code HandlerResult} so that may also be applied later
|
||||
* after result handling.
|
||||
*
|
||||
* @param request current request
|
||||
* @param response current response
|
||||
* @param handler the selected handler which must have been previously
|
||||
* checked via {@link #supports(Object)}
|
||||
* @return {@link Mono} that emits a single {@code HandlerResult} or none if
|
||||
* the request has been fully handled and doesn't require further handling.
|
||||
*/
|
||||
Mono<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response,
|
||||
Object handler);
|
||||
|
||||
@@ -36,9 +36,7 @@ public class HandlerResult {
|
||||
|
||||
private final ResolvableType resultType;
|
||||
|
||||
private final Throwable error;
|
||||
|
||||
private Function<Throwable, Mono<HandlerResult>> exceptionMapper;
|
||||
private Function<Throwable, Mono<HandlerResult>> exceptionHandler;
|
||||
|
||||
|
||||
public HandlerResult(Object handler, Object result, ResolvableType resultType) {
|
||||
@@ -47,16 +45,6 @@ public class HandlerResult {
|
||||
this.handler = handler;
|
||||
this.result = result;
|
||||
this.resultType = resultType;
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
public HandlerResult(Object handler, Throwable error) {
|
||||
Assert.notNull(handler, "'handler' is required");
|
||||
Assert.notNull(error, "'error' is required");
|
||||
this.handler = handler;
|
||||
this.result = null;
|
||||
this.resultType = null;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,38 +60,25 @@ public class HandlerResult {
|
||||
return this.resultType;
|
||||
}
|
||||
|
||||
public Throwable getError() {
|
||||
return this.error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether handler invocation produced a result or failed with an error.
|
||||
* <p>If {@code true} the {@link #getError()} returns the error while
|
||||
* {@link #getResult()} and {@link #getResultType()} return {@code null}
|
||||
* and vice versa.
|
||||
* @return whether this instance contains a result or an error.
|
||||
* For an async result, failures may occur later during result handling.
|
||||
* Use this property to configure an exception handler to be invoked if
|
||||
* result handling fails.
|
||||
*
|
||||
* @param function a function to map the the error to an alternative result.
|
||||
* @return the current instance
|
||||
*/
|
||||
public boolean hasError() {
|
||||
return (this.error != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a function for selecting an alternate {@code HandlerResult} in
|
||||
* case of an {@link #hasError() error result} or in case of an async result
|
||||
* that results in an error.
|
||||
* @param function the exception resolving function
|
||||
*/
|
||||
public HandlerResult setExceptionMapper(Function<Throwable, Mono<HandlerResult>> function) {
|
||||
this.exceptionMapper = function;
|
||||
public HandlerResult setExceptionHandler(Function<Throwable, Mono<HandlerResult>> function) {
|
||||
this.exceptionHandler = function;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Function<Throwable, Mono<HandlerResult>> getExceptionMapper() {
|
||||
return this.exceptionMapper;
|
||||
public boolean hasExceptionHandler() {
|
||||
return (this.exceptionHandler != null);
|
||||
}
|
||||
|
||||
public boolean hasExceptionMapper() {
|
||||
return (this.exceptionMapper != null);
|
||||
public Mono<HandlerResult> applyExceptionHandler(Throwable ex) {
|
||||
return (hasExceptionHandler() ? this.exceptionHandler.apply(ex) : Mono.error(ex));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -109,17 +109,15 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin
|
||||
Object handler) {
|
||||
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
|
||||
InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod);
|
||||
invocable.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
|
||||
return invocable.invokeForRequest(request)
|
||||
.otherwise(ex -> Mono.just(new HandlerResult(handler, ex)))
|
||||
.map(result -> result.setExceptionMapper(
|
||||
ex -> mapException(ex, handlerMethod, request, response)));
|
||||
.map(result -> result.setExceptionHandler(ex -> handleException(ex, handlerMethod, request, response)))
|
||||
.otherwise(ex -> handleException(ex, handlerMethod, request, response));
|
||||
}
|
||||
|
||||
private Mono<HandlerResult> mapException(Throwable ex, HandlerMethod handlerMethod,
|
||||
private Mono<HandlerResult> handleException(Throwable ex, HandlerMethod handlerMethod,
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (ex instanceof Exception) {
|
||||
|
||||
Reference in New Issue
Block a user