Browse Source

Refine exception handling

Refine exception handling responsibilities between the
DispatcherHandler and the HandlerAdapter.
pull/1111/head
Rossen Stoyanchev 10 years ago
parent
commit
97af9998d5
  1. 28
      spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java
  2. 45
      spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerAdapter.java
  3. 51
      spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerResult.java
  4. 8
      spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerAdapter.java

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

@ -117,35 +117,29 @@ public class DispatcherHandler implements HttpHandler, ApplicationContextAware { @@ -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;

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

@ -16,17 +16,16 @@ @@ -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; @@ -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);

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

@ -36,9 +36,7 @@ public class HandlerResult { @@ -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 { @@ -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 { @@ -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.
*/
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
* 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 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));
}
}

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

@ -109,17 +109,15 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin @@ -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) {

Loading…
Cancel
Save