diff --git a/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Decoder.java b/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Decoder.java index 337cb245125..b17150ed9bf 100644 --- a/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Decoder.java +++ b/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Decoder.java @@ -38,8 +38,8 @@ import reactor.Flux; import org.springframework.core.ResolvableType; import org.springframework.core.codec.CodecException; -import org.springframework.util.ByteBufferPublisherInputStream; import org.springframework.util.Assert; +import org.springframework.util.ByteBufferPublisherInputStream; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; diff --git a/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Encoder.java b/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Encoder.java index f518d9f5e26..d692c744fff 100644 --- a/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Encoder.java +++ b/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Encoder.java @@ -31,8 +31,8 @@ import reactor.io.buffer.Buffer; import org.springframework.core.ResolvableType; import org.springframework.core.codec.CodecException; -import org.springframework.util.BufferOutputStream; import org.springframework.util.Assert; +import org.springframework.util.BufferOutputStream; import org.springframework.util.ClassUtils; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java index f4ee9f3dcf6..7e4683eed50 100644 --- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java +++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java @@ -22,7 +22,6 @@ import java.nio.ByteBuffer; import io.netty.buffer.ByteBuf; import io.reactivex.netty.protocol.http.server.HttpServerRequest; -import org.reactivestreams.Publisher; import reactor.Flux; import reactor.core.publisher.convert.RxJava1Converter; import rx.Observable; diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/JettyHttpServer.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/JettyHttpServer.java index d6780ae19c1..f462a8ad207 100644 --- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/JettyHttpServer.java +++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/JettyHttpServer.java @@ -22,9 +22,9 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.springframework.beans.factory.InitializingBean; +import org.springframework.http.server.reactive.ServletHttpHandlerAdapter; import org.springframework.util.Assert; import org.springframework.util.SocketUtils; -import org.springframework.http.server.reactive.ServletHttpHandlerAdapter; /** * @author Rossen Stoyanchev diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/ReactorHttpServer.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/ReactorHttpServer.java index c335796d0ce..e575fc82b88 100644 --- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/ReactorHttpServer.java +++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/ReactorHttpServer.java @@ -21,8 +21,8 @@ import reactor.io.buffer.Buffer; import reactor.io.net.ReactiveNet; import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; +import org.springframework.util.Assert; /** * @author Stephane Maldini diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/RxNettyHttpServer.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/RxNettyHttpServer.java index 84525ac0d57..24c056f3f06 100644 --- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/RxNettyHttpServer.java +++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/RxNettyHttpServer.java @@ -19,8 +19,8 @@ package org.springframework.http.server.reactive.boot; import io.netty.buffer.ByteBuf; import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; import org.springframework.http.server.reactive.RxNettyHttpHandlerAdapter; +import org.springframework.util.Assert; /** diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/TomcatHttpServer.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/TomcatHttpServer.java index b91c3aa49aa..90c75057c88 100644 --- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/TomcatHttpServer.java +++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/TomcatHttpServer.java @@ -23,9 +23,9 @@ import org.apache.catalina.LifecycleException; import org.apache.catalina.startup.Tomcat; import org.springframework.beans.factory.InitializingBean; +import org.springframework.http.server.reactive.ServletHttpHandlerAdapter; import org.springframework.util.Assert; import org.springframework.util.SocketUtils; -import org.springframework.http.server.reactive.ServletHttpHandlerAdapter; /** diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/UndertowHttpServer.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/UndertowHttpServer.java index 68af14ae4ac..3de5b5da1e8 100644 --- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/UndertowHttpServer.java +++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/boot/UndertowHttpServer.java @@ -16,13 +16,13 @@ package org.springframework.http.server.reactive.boot; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; -import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter; - import io.undertow.Undertow; import io.undertow.server.HttpHandler; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter; +import org.springframework.util.Assert; + /** * @author Marek Hawrylczak */ diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java index d5fbe851690..cb99099a719 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java @@ -31,9 +31,9 @@ import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.WebHandler; +import org.springframework.web.server.WebServerExchange; /** * Central dispatcher for HTTP request handlers/controllers. Dispatches to registered @@ -53,7 +53,7 @@ import org.springframework.http.server.reactive.ServerHttpResponse; * @author Rossen Stoyanchev * @author Sebastien Deleuze */ -public class DispatcherHandler implements HttpHandler, ApplicationContextAware { +public class DispatcherHandler implements WebHandler, ApplicationContextAware { private static final Log logger = LogFactory.getLog(DispatcherHandler.class); @@ -112,31 +112,32 @@ public class DispatcherHandler implements HttpHandler, ApplicationContextAware { @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { + public Mono handle(WebServerExchange exchange) { if (logger.isDebugEnabled()) { + ServerHttpRequest request = exchange.getRequest(); logger.debug("Processing " + request.getMethod() + " request for [" + request.getURI() + "]"); } return Flux.fromIterable(this.handlerMappings) - .concatMap(mapping -> mapping.getHandler(request)) + .concatMap(mapping -> mapping.getHandler(exchange)) .next() - .then(handler -> invokeHandler(request, response, handler)) - .then(result -> handleResult(request, response, result)) + .then(handler -> invokeHandler(exchange, handler)) + .then(result -> handleResult(exchange, result)) .otherwise(ex -> Mono.error(this.errorMapper.apply(ex))); } - private Mono invokeHandler(ServerHttpRequest request, ServerHttpResponse response, Object handler) { + private Mono invokeHandler(WebServerExchange exchange, Object handler) { for (HandlerAdapter handlerAdapter : this.handlerAdapters) { if (handlerAdapter.supports(handler)) { - return handlerAdapter.handle(request, response, handler); + return handlerAdapter.handle(exchange, handler); } } return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler)); } - private Mono handleResult(ServerHttpRequest request, ServerHttpResponse response, HandlerResult result) { - return getResultHandler(result).handleResult(request, response, result) + private Mono handleResult(WebServerExchange exchange, HandlerResult result) { + return getResultHandler(result).handleResult(exchange, result) .otherwise(ex -> result.applyExceptionHandler(ex).then(exceptionResult -> - getResultHandler(result).handleResult(request, response, exceptionResult))); + getResultHandler(result).handleResult(exchange, exceptionResult))); } private HandlerResultHandler getResultHandler(HandlerResult handlerResult) { @@ -156,7 +157,7 @@ public class DispatcherHandler implements HttpHandler, ApplicationContextAware { @Override - public Mono getHandler(ServerHttpRequest request) { + public Mono getHandler(WebServerExchange exchange) { return Mono.error(HANDLER_NOT_FOUND_EXCEPTION); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandlerExceptionMapper.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandlerExceptionMapper.java index 9884aa122d0..07a39d735f7 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandlerExceptionMapper.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandlerExceptionMapper.java @@ -19,8 +19,8 @@ import java.util.function.Function; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.http.HttpStatus; -import org.springframework.web.ResponseStatusException; import org.springframework.web.HttpMediaTypeNotAcceptableException; +import org.springframework.web.ResponseStatusException; import org.springframework.web.bind.annotation.ResponseStatus; /** diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerAdapter.java index 12623033804..ad961582642 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerAdapter.java @@ -20,8 +20,7 @@ import java.util.function.Function; import reactor.Mono; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.WebServerExchange; /** * Contract that decouples the {@link DispatcherHandler} from the details of @@ -53,14 +52,12 @@ public interface HandlerAdapter { * handler} on the {@code HandlerResult} so that may also be applied later * after result handling. * - * @param request current request - * @param response current response + * @param exchange current server exchange * @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 handle(ServerHttpRequest request, ServerHttpResponse response, - Object handler); + Mono handle(WebServerExchange exchange, Object handler); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerMapping.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerMapping.java index b8a164421a9..d3c6f9ab6ed 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerMapping.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerMapping.java @@ -18,7 +18,7 @@ package org.springframework.web.reactive; import reactor.Mono; -import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.WebServerExchange; /** * Interface to be implemented by objects that define a mapping between @@ -31,10 +31,10 @@ public interface HandlerMapping { /** * Return a handler for this request. - * @param request current HTTP request + * @param exchange current server exchange * @return A {@link Mono} that emits one value or none in case the request - * cannot be resolved to a handler. + * cannot be resolved to a handler */ - Mono getHandler(ServerHttpRequest request); + Mono getHandler(WebServerExchange exchange); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerResultHandler.java index fe55814ac3c..d5a668a1eff 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerResultHandler.java @@ -18,8 +18,7 @@ package org.springframework.web.reactive; import reactor.Mono; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.WebServerExchange; /** * Process the {@link HandlerResult}, usually returned by an {@link HandlerAdapter}. @@ -41,9 +40,10 @@ public interface HandlerResultHandler { * Process the given result modifying response headers and/or writing data * to the response. * + * @param exchange current server exchange + * @param result the result from the handling * @return {@code Mono} to indicate when request handling is complete. */ - Mono handleResult(ServerHttpRequest request, ServerHttpResponse response, - HandlerResult result); + Mono handleResult(WebServerExchange exchange, HandlerResult result); } \ No newline at end of file diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/ResponseStatusExceptionHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/ResponseStatusExceptionHandler.java index 23127784966..8c19629d6c1 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/ResponseStatusExceptionHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/ResponseStatusExceptionHandler.java @@ -17,23 +17,22 @@ package org.springframework.web.reactive; import reactor.Mono; -import org.springframework.web.server.HttpExceptionHandler; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.ResponseStatusException; +import org.springframework.web.server.WebExceptionHandler; +import org.springframework.web.server.WebServerExchange; /** * Handle {@link ResponseStatusException} by setting the response status. * * @author Rossen Stoyanchev */ -public class ResponseStatusExceptionHandler implements HttpExceptionHandler { +public class ResponseStatusExceptionHandler implements WebExceptionHandler { @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) { + public Mono handle(WebServerExchange exchange, Throwable ex) { if (ex instanceof ResponseStatusException) { - response.setStatusCode(((ResponseStatusException) ex).getHttpStatus()); + exchange.getResponse().setStatusCode(((ResponseStatusException) ex).getHttpStatus()); return Mono.empty(); } return Mono.error(ex); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/HttpHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/HttpHandlerHandlerAdapter.java similarity index 56% rename from spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/HttpHandlerAdapter.java rename to spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/HttpHandlerHandlerAdapter.java index 13694ce1d7e..b4994deb70c 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/HttpHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/HttpHandlerHandlerAdapter.java @@ -20,25 +20,20 @@ import org.reactivestreams.Publisher; import reactor.Mono; import org.springframework.core.ResolvableType; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.HandlerAdapter; import org.springframework.web.reactive.HandlerResult; -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.web.reactive.DispatcherHandler; +import org.springframework.web.server.WebHandler; +import org.springframework.web.server.WebServerExchange; /** - * Support use of {@link HttpHandler} with - * {@link DispatcherHandler - * DispatcherHandler} (which implements the same contract). - * The use of {@code DispatcherHandler} this way enables routing requests to - * one of many {@code HttpHandler} instances depending on the configured - * handler mappings. + * Support use of {@link org.springframework.web.server.WebHandler} through the + * {@link DispatcherHandler}. * * @author Rossen Stoyanchev * @author Sebastien Deleuze */ -public class HttpHandlerAdapter implements HandlerAdapter { +public class HttpHandlerHandlerAdapter implements HandlerAdapter { private static final ResolvableType PUBLISHER_VOID = ResolvableType.forClassWithGenerics( Publisher.class, Void.class); @@ -46,14 +41,14 @@ public class HttpHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { - return HttpHandler.class.isAssignableFrom(handler.getClass()); + return WebHandler.class.isAssignableFrom(handler.getClass()); } @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response, Object handler) { - HttpHandler httpHandler = (HttpHandler)handler; - Mono completion = httpHandler.handle(request, response); - return Mono.just(new HandlerResult(httpHandler, completion, PUBLISHER_VOID)); + public Mono handle(WebServerExchange exchange, Object handler) { + WebHandler webHandler = (WebHandler) handler; + Mono completion = webHandler.handle(exchange); + return Mono.just(new HandlerResult(webHandler, completion, PUBLISHER_VOID)); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleHandlerResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleHandlerResultHandler.java index ba03013652d..6c8494ff1d7 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleHandlerResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleHandlerResultHandler.java @@ -22,11 +22,10 @@ import reactor.Mono; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; import org.springframework.core.convert.ConversionService; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResultHandler; +import org.springframework.web.server.WebServerExchange; /** * Supports {@link HandlerResult} with a {@code void} or {@code Publisher} value. @@ -75,15 +74,11 @@ public class SimpleHandlerResultHandler implements Ordered, HandlerResultHandler @SuppressWarnings("unchecked") @Override - public Mono handleResult(ServerHttpRequest request, - ServerHttpResponse response, HandlerResult result) { - + public Mono handleResult(WebServerExchange exchange, HandlerResult result) { Object value = result.getResult(); - if (Void.TYPE.equals(result.getResultType().getRawClass())) { return Mono.empty(); } - return (value instanceof Mono ? (Mono)value : Mono.from(this.conversionService.convert(value, Publisher.class))); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java index 0ef5e9c286a..5ff03ffdafe 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java @@ -22,8 +22,8 @@ import java.util.Map; import reactor.Flux; import reactor.Mono; -import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.server.WebServerExchange; /** * @author Rossen Stoyanchev @@ -42,9 +42,9 @@ public class SimpleUrlHandlerMapping implements HandlerMapping { @Override - public Mono getHandler(ServerHttpRequest request) { + public Mono getHandler(WebServerExchange exchange) { return Flux.create(subscriber -> { - String path = request.getURI().getPath(); + String path = exchange.getRequest().getURI().getPath(); Object handler = this.handlerMap.get(path); if (handler != null) { subscriber.onNext(handler); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/HandlerMethodArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/HandlerMethodArgumentResolver.java index b4e7fd68e7f..c9fc9397c1f 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/HandlerMethodArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/HandlerMethodArgumentResolver.java @@ -19,7 +19,7 @@ package org.springframework.web.reactive.method; import reactor.Mono; import org.springframework.core.MethodParameter; -import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.WebServerExchange; /** @@ -35,6 +35,6 @@ public interface HandlerMethodArgumentResolver { * does not resolve to any value, which will result in {@code null} passed * as the argument value. */ - Mono resolveArgument(MethodParameter parameter, ServerHttpRequest request); + Mono resolveArgument(MethodParameter parameter, WebServerExchange exchange); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/InvocableHandlerMethod.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/InvocableHandlerMethod.java index 0aaa7a67579..1b30d6da7ed 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/InvocableHandlerMethod.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/InvocableHandlerMethod.java @@ -32,11 +32,11 @@ import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.ResolvableType; -import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.HandlerResult; +import org.springframework.web.server.WebServerExchange; /** @@ -76,14 +76,14 @@ public class InvocableHandlerMethod extends HandlerMethod { /** * Invoke the method and return a Publisher for the return value. - * @param request the current request + * @param exchange the current exchange * @param providedArgs optional list of argument values to check by type * (via {@code instanceof}) for resolving method arguments. * @return Publisher that produces a single HandlerResult or an error signal; - * never throws an exception. + * never throws an exception */ - public Mono invokeForRequest(ServerHttpRequest request, Object... providedArgs) { - return resolveArguments(request, providedArgs).then(args -> { + public Mono invokeForRequest(WebServerExchange exchange, Object... providedArgs) { + return resolveArguments(exchange, providedArgs).then(args -> { try { Object value = doInvoke(args); ResolvableType type = ResolvableType.forMethodParameter(getReturnType()); @@ -100,7 +100,7 @@ public class InvocableHandlerMethod extends HandlerMethod { }); } - private Mono resolveArguments(ServerHttpRequest request, Object... providedArgs) { + private Mono resolveArguments(WebServerExchange exchange, Object... providedArgs) { if (ObjectUtils.isEmpty(getMethodParameters())) { return NO_ARGS; } @@ -121,7 +121,7 @@ public class InvocableHandlerMethod extends HandlerMethod { .findFirst() .orElseThrow(() -> getArgError("No resolver for ", param, null)); try { - return resolver.resolveArgument(param, request) + return resolver.resolveArgument(param, exchange) .defaultIfEmpty(NO_VALUE) .otherwise(ex -> Mono.error(getArgError("Error resolving ", param, ex))); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestBodyArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestBodyArgumentResolver.java index 9302e333a69..3e5c01306bb 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestBodyArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestBodyArgumentResolver.java @@ -25,13 +25,13 @@ import reactor.Mono; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; +import org.springframework.core.codec.Decoder; import org.springframework.core.convert.ConversionService; import org.springframework.http.MediaType; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.core.codec.Decoder; -import org.springframework.web.reactive.method.HandlerMethodArgumentResolver; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.reactive.method.HandlerMethodArgumentResolver; +import org.springframework.web.server.WebServerExchange; /** * @author Sebastien Deleuze @@ -58,13 +58,13 @@ public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolve } @Override - public Mono resolveArgument(MethodParameter parameter, ServerHttpRequest request) { - MediaType mediaType = request.getHeaders().getContentType(); + public Mono resolveArgument(MethodParameter parameter, WebServerExchange exchange) { + MediaType mediaType = exchange.getRequest().getHeaders().getContentType(); if (mediaType == null) { mediaType = MediaType.APPLICATION_OCTET_STREAM; } ResolvableType type = ResolvableType.forMethodParameter(parameter); - Flux body = request.getBody(); + Flux body = exchange.getRequest().getBody(); Flux elementFlux = body; ResolvableType elementType = type.hasGenerics() ? type.getGeneric(0) : type; diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerAdapter.java index b9ebe323ead..51d6539d309 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerAdapter.java @@ -35,8 +35,6 @@ import org.springframework.core.codec.support.JsonObjectDecoder; import org.springframework.core.codec.support.StringDecoder; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.ObjectUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver; @@ -44,6 +42,7 @@ import org.springframework.web.reactive.HandlerAdapter; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.method.HandlerMethodArgumentResolver; import org.springframework.web.reactive.method.InvocableHandlerMethod; +import org.springframework.web.server.WebServerExchange; /** @@ -105,20 +104,18 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin } @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response, - Object handler) { - + public Mono handle(WebServerExchange exchange, Object handler) { HandlerMethod handlerMethod = (HandlerMethod) handler; InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod); invocable.setHandlerMethodArgumentResolvers(this.argumentResolvers); - return invocable.invokeForRequest(request) - .map(result -> result.setExceptionHandler(ex -> handleException(ex, handlerMethod, request, response))) - .otherwise(ex -> handleException(ex, handlerMethod, request, response)); + return invocable.invokeForRequest(exchange) + .map(result -> result.setExceptionHandler(ex -> handleException(ex, handlerMethod, exchange))) + .otherwise(ex -> handleException(ex, handlerMethod, exchange)); } private Mono handleException(Throwable ex, HandlerMethod handlerMethod, - ServerHttpRequest request, ServerHttpResponse response) { + WebServerExchange exchange) { if (ex instanceof Exception) { InvocableHandlerMethod invocable = findExceptionHandler(handlerMethod, (Exception) ex); @@ -128,7 +125,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin logger.debug("Invoking @ExceptionHandler method: " + invocable); } invocable.setHandlerMethodArgumentResolvers(getArgumentResolvers()); - return invocable.invokeForRequest(request, response, ex); + return invocable.invokeForRequest(exchange, ex); } catch (Exception invocationEx) { if (logger.isErrorEnabled()) { diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMapping.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMapping.java index 476b0bc8f97..dff2c391e78 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMapping.java @@ -41,6 +41,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethodSelector; import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.server.WebServerExchange; /** @@ -65,9 +66,7 @@ public class RequestMappingHandlerMapping implements HandlerMapping, @Override public void afterPropertiesSet() throws Exception { - for (Object bean : this.applicationContext.getBeansOfType(Object.class).values()) { - detectHandlerMethods(bean); - } + this.applicationContext.getBeansOfType(Object.class).values().forEach(this::detectHandlerMethods); } protected void detectHandlerMethods(final Object bean) { @@ -94,15 +93,15 @@ public class RequestMappingHandlerMapping implements HandlerMapping, } @Override - public Mono getHandler(ServerHttpRequest request) { + public Mono getHandler(WebServerExchange exchange) { return Flux.create(subscriber -> { for (Map.Entry entry : this.methodMap.entrySet()) { RequestMappingInfo info = entry.getKey(); - if (info.matchesRequest(request)) { + if (info.matchesRequest(exchange.getRequest())) { HandlerMethod handlerMethod = entry.getValue(); if (logger.isDebugEnabled()) { - logger.debug("Mapped " + request.getMethod() + " " + - request.getURI().getPath() + " to [" + handlerMethod + "]"); + logger.debug("Mapped " + exchange.getRequest().getMethod() + " " + + exchange.getRequest().getURI().getPath() + " to [" + handlerMethod + "]"); } subscriber.onNext(handlerMethod); break; diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestParamArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestParamArgumentResolver.java index 8ba175976bd..8c056f4c37f 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestParamArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestParamArgumentResolver.java @@ -19,9 +19,9 @@ package org.springframework.web.reactive.method.annotation; import reactor.Mono; import org.springframework.core.MethodParameter; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.web.reactive.method.HandlerMethodArgumentResolver; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.reactive.method.HandlerMethodArgumentResolver; +import org.springframework.web.server.WebServerExchange; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; @@ -41,10 +41,10 @@ public class RequestParamArgumentResolver implements HandlerMethodArgumentResolv @Override - public Mono resolveArgument(MethodParameter param, ServerHttpRequest request) { + public Mono resolveArgument(MethodParameter param, WebServerExchange exchange) { RequestParam annotation = param.getParameterAnnotation(RequestParam.class); String name = (annotation.value().length() != 0 ? annotation.value() : param.getParameterName()); - UriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI()).build(); + UriComponents uriComponents = UriComponentsBuilder.fromUri(exchange.getRequest().getURI()).build(); String value = uriComponents.getQueryParams().getFirst(name); return (value != null ? Mono.just(value) : Mono.empty()); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/ResponseBodyResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/ResponseBodyResultHandler.java index 05e3a2adc06..0d9422266f9 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/ResponseBodyResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/ResponseBodyResultHandler.java @@ -33,11 +33,11 @@ import reactor.Mono; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.codec.Encoder; import org.springframework.core.convert.ConversionService; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.core.codec.Encoder; import org.springframework.util.Assert; import org.springframework.util.MimeType; import org.springframework.web.HttpMediaTypeNotAcceptableException; @@ -45,6 +45,7 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResultHandler; +import org.springframework.web.server.WebServerExchange; /** @@ -127,8 +128,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered @Override @SuppressWarnings("unchecked") - public Mono handleResult(ServerHttpRequest request, - ServerHttpResponse response, HandlerResult result) { + public Mono handleResult(WebServerExchange exchange, HandlerResult result) { Object value = result.getResult(); if (value == null) { @@ -147,7 +147,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered elementType = returnType; } - List requestedMediaTypes = getAcceptableMediaTypes(request); + List requestedMediaTypes = getAcceptableMediaTypes(exchange.getRequest()); List producibleMediaTypes = getProducibleMediaTypes(elementType); if (producibleMediaTypes.isEmpty()) { @@ -184,6 +184,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered if (selectedMediaType != null) { Encoder encoder = resolveEncoder(elementType, selectedMediaType); if (encoder != null) { + ServerHttpResponse response = exchange.getResponse(); response.getHeaders().setContentType(selectedMediaType); return response.setBody(encoder.encode((Publisher) publisher, elementType, selectedMediaType)); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/DefaultWebServerExchange.java b/spring-web-reactive/src/main/java/org/springframework/web/server/DefaultWebServerExchange.java new file mode 100644 index 00000000000..fe5cb4f5db6 --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/DefaultWebServerExchange.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.server; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.Assert; + +/** + * Default implementation of {@link WebServerExchange}. + * + * @author Rossen Stoyanchev + */ +public class DefaultWebServerExchange implements WebServerExchange { + + private final ServerHttpRequest request; + + private final ServerHttpResponse response; + + private final Map attributes = new ConcurrentHashMap<>(); + + + public DefaultWebServerExchange(ServerHttpRequest request, ServerHttpResponse response) { + Assert.notNull(request, "'request' is required."); + Assert.notNull(response, "'response' is required."); + this.request = request; + this.response = response; + } + + + @Override + public ServerHttpRequest getRequest() { + return this.request; + } + + @Override + public ServerHttpResponse getResponse() { + return this.response; + } + + @Override + public Map getAttributes() { + return this.attributes; + } + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/ErrorHandlingHttpHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/ErrorHandlingHttpHandler.java deleted file mode 100644 index 3edbcfc50e4..00000000000 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/ErrorHandlingHttpHandler.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2002-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.web.server; - -import java.util.Arrays; -import java.util.List; - -import reactor.Mono; - -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.util.Assert; - -/** - * {@link HttpHandler} that delegates to a target {@link HttpHandler} and handles - * any errors from it by invoking one or more {@link HttpExceptionHandler}s - * sequentially until one of them completes successfully. - * - * @author Rossen Stoyanchev - * @author Stephane Maldini - */ -public class ErrorHandlingHttpHandler extends HttpHandlerDecorator { - - private final List exceptionHandlers; - - - public ErrorHandlingHttpHandler(HttpHandler targetHandler, HttpExceptionHandler... exceptionHandlers) { - super(targetHandler); - Assert.notEmpty(exceptionHandlers, "At least one exception handler is required"); - this.exceptionHandlers = Arrays.asList(exceptionHandlers); - } - - - @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { - Mono mono; - try { - mono = getDelegate().handle(request, response); - } - catch (Throwable ex) { - mono = Mono.error(ex); - } - for (HttpExceptionHandler handler : this.exceptionHandlers) { - mono = applyExceptionHandler(mono, handler, request, response); - } - return mono; - } - - private static Mono applyExceptionHandler(Mono mono, HttpExceptionHandler handler, - ServerHttpRequest request, ServerHttpResponse response) { - - return mono.otherwise(ex -> handler.handle(request, response, ex)).after(); - } - -} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/ExceptionHandlingWebHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/ExceptionHandlingWebHandler.java new file mode 100644 index 00000000000..2842bc801b6 --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/ExceptionHandlingWebHandler.java @@ -0,0 +1,74 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.server; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import reactor.Mono; + +import org.springframework.http.HttpStatus; + +/** + * {@code WebHandler} that decorates another with exception handling using one + * or more instances of {@link WebExceptionHandler}. + * + * @author Rossen Stoyanchev + */ +public class ExceptionHandlingWebHandler extends WebHandlerDecorator { + + private final List exceptionHandlers; + + + public ExceptionHandlingWebHandler(WebHandler delegate, WebExceptionHandler... exceptionHandlers) { + super(delegate); + this.exceptionHandlers = initList(exceptionHandlers); + } + + private static List initList(WebExceptionHandler[] list) { + return (list != null ? Collections.unmodifiableList(Arrays.asList(list)): Collections.emptyList()); + } + + + /** + * @return a read-only list of the configured exception handlers. + */ + public List getExceptionHandlers() { + return this.exceptionHandlers; + } + + @Override + public Mono handle(WebServerExchange exchange) { + Mono mono; + try { + mono = getDelegate().handle(exchange); + } + catch (Throwable ex) { + mono = Mono.error(ex); + } + for (WebExceptionHandler exceptionHandler : this.exceptionHandlers) { + mono = mono.otherwise(ex -> exceptionHandler.handle(exchange, ex)); + } + return mono.otherwise(ex -> handleUnresolvedException(exchange)); + } + + private Mono handleUnresolvedException(WebServerExchange exchange) { + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + return Mono.empty(); + } + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/FilterChainHttpHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/FilterChainHttpHandler.java deleted file mode 100644 index 2026ab3c076..00000000000 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/FilterChainHttpHandler.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2002-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.web.server; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import reactor.Mono; - -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; - -/** - * {@link HttpHandler} that delegates to a chain of {@link HttpFilter}s followed - * by a target {@link HttpHandler}. - * - * @author Rossen Stoyanchev - */ -public class FilterChainHttpHandler extends HttpHandlerDecorator { - - private final List filters; - - - public FilterChainHttpHandler(HttpHandler targetHandler, HttpFilter... filters) { - super(targetHandler); - this.filters = (filters != null ? Arrays.asList(filters) : Collections.emptyList()); - } - - - @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { - return new DefaultHttpFilterChain().filter(request, response); - } - - - private class DefaultHttpFilterChain implements HttpFilterChain { - - private int index; - - @Override - public Mono filter(ServerHttpRequest request, ServerHttpResponse response) { - if (this.index < filters.size()) { - HttpFilter filter = filters.get(this.index++); - return filter.filter(request, response, this); - } - else { - return getDelegate().handle(request, response); - } - } - } - -} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/FilteringWebHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/FilteringWebHandler.java new file mode 100644 index 00000000000..128bbed47e8 --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/FilteringWebHandler.java @@ -0,0 +1,74 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.server; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import reactor.Mono; + +/** + * {@code WebHandler} that decorates another with a chain of {@link WebFilter}s. + * + * @author Rossen Stoyanchev + */ +public class FilteringWebHandler extends WebHandlerDecorator { + + private final List filters; + + + public FilteringWebHandler(WebHandler targetHandler, WebFilter... filters) { + super(targetHandler); + this.filters = initList(filters); + } + + private static List initList(WebFilter[] list) { + return (list != null ? Collections.unmodifiableList(Arrays.asList(list)): Collections.emptyList()); + } + + + /** + * @return a read-only list of the configured filters. + */ + public List getFilters() { + return this.filters; + } + + @Override + public Mono handle(WebServerExchange exchange) { + return new DefaultWebFilterChain().filter(exchange); + } + + + private class DefaultWebFilterChain implements WebFilterChain { + + private int index; + + + @Override + public Mono filter(WebServerExchange exchange) { + if (this.index < filters.size()) { + WebFilter filter = filters.get(this.index++); + return filter.filter(exchange, this); + } + else { + return getDelegate().handle(exchange); + } + } + } + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/HttpFilter.java b/spring-web-reactive/src/main/java/org/springframework/web/server/HttpFilter.java deleted file mode 100644 index e1433a26f79..00000000000 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/HttpFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2002-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.server; - -import reactor.Mono; - -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; - -/** - * Contract for interception-style, chained processing of HTTP requests. - * - *

Filters may be used to implement cross-cutting, application-agnostic - * requirements such as security, timeouts, and others. - * - *

{@link FilterChainHttpHandler} provides a way of constructing a chain of - * {@link HttpFilter}s followed by a target {@link HttpHandler}. - * - * @author Rossen Stoyanchev - * @see FilterChainHttpHandler - */ -public interface HttpFilter { - - /** - * Process the given request and optionally delegate to the next HttpFilter. - * - * @param request current HTTP request. - * @param response current HTTP response. - * @param chain provides a way to delegate to the next HttpFilter. - * @return {@code Mono} to indicate when request processing is complete. - */ - Mono filter(ServerHttpRequest request, ServerHttpResponse response, - HttpFilterChain chain); - -} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/WebExceptionHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebExceptionHandler.java new file mode 100644 index 00000000000..84c12a65c5a --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebExceptionHandler.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.server; + +import reactor.Mono; + +/** + * Contract for handling exceptions during web server exchange processing. + * + * @author Rossen Stoyanchev + */ +public interface WebExceptionHandler { + + /** + * Handle the given exception. A completion signal through the return value + * indicates error handling is complete while an error signal indicates the + * exception is still not handled. + * + * @param exchange the current exchange + * @param ex the exception to handle + * @return {@code Mono} to indicate when exception handling is complete + */ + Mono handle(WebServerExchange exchange, Throwable ex); + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/WebFilter.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebFilter.java new file mode 100644 index 00000000000..06c8b5e85c9 --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebFilter.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.server; + +import reactor.Mono; + +/** + * Contract for interception-style, chained processing of Web requests that may + * be used to implement cross-cutting, application-agnostic requirements such + * as security, timeouts, and others. + * + * @author Rossen Stoyanchev + */ +public interface WebFilter { + + /** + * Process the Web request and (optionally) delegate to the next + * {@code WebFilter} through the given {@link WebFilterChain}. + * + * @param exchange the current server exchange + * @param chain provides a way to delegate to the next filter + * @return {@code Mono} to indicate when request processing is complete + */ + Mono filter(WebServerExchange exchange, WebFilterChain chain); + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/HttpFilterChain.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebFilterChain.java similarity index 63% rename from spring-web-reactive/src/main/java/org/springframework/web/server/HttpFilterChain.java rename to spring-web-reactive/src/main/java/org/springframework/web/server/WebFilterChain.java index 5188785610b..b071f97e739 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/HttpFilterChain.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebFilterChain.java @@ -17,23 +17,19 @@ package org.springframework.web.server; import reactor.Mono; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; - /** - * Represents a chain of {@link HttpFilter}s allowing each {@link HttpFilter} to - * delegate to the next in the chain. + * Contract to allow a {@link WebFilter} to delegate to the next in the chain. * * @author Rossen Stoyanchev */ -public interface HttpFilterChain { +public interface WebFilterChain { /** + * Delegate to the next {@code WebFilter} in the chain. * - * @param request current HTTP request. - * @param response current HTTP response. - * @return {@code Mono} to indicate when request handling is complete. + * @param exchange the current server exchange + * @return {@code Mono} to indicate when request handling is complete */ - Mono filter(ServerHttpRequest request, ServerHttpResponse response); + Mono filter(WebServerExchange exchange); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java new file mode 100644 index 00000000000..58cb9286fb7 --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.server; + +import reactor.Mono; + +/** + * Contract to handle a web server exchange. + * + *

Use {@link WebToHttpHandlerAdapter} to adapt a {@code WebHandler} to an + * {@link org.springframework.http.server.reactive.HttpHandler HttpHandler}. + * The {@link WebToHttpHandlerBuilder} provides a convenient way to do that while + * also optionally configuring one or more filters and/or exception handlers. + * + * @author Rossen Stoyanchev + * @see WebToHttpHandlerBuilder + */ +public interface WebHandler { + + /** + * Handle the web server exchange. + * + * @param exchange the current server exchange + * @return {@code Mono} to indicate when request handling is complete + */ + Mono handle(WebServerExchange exchange); + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/HttpHandlerDecorator.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandlerDecorator.java similarity index 66% rename from spring-web-reactive/src/main/java/org/springframework/web/server/HttpHandlerDecorator.java rename to spring-web-reactive/src/main/java/org/springframework/web/server/WebHandlerDecorator.java index c2062755ce2..42cc20e3ece 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/HttpHandlerDecorator.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandlerDecorator.java @@ -17,34 +17,32 @@ package org.springframework.web.server; import reactor.Mono; -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; /** + * Base class for a {@link WebHandler} that decorates and delegates to another. * * @author Rossen Stoyanchev */ -public class HttpHandlerDecorator implements HttpHandler { +public class WebHandlerDecorator implements WebHandler { - private final HttpHandler delegate; + private final WebHandler delegate; - public HttpHandlerDecorator(HttpHandler delegate) { + public WebHandlerDecorator(WebHandler delegate) { Assert.notNull(delegate, "'delegate' must not be null"); this.delegate = delegate; } - public HttpHandler getDelegate() { + public WebHandler getDelegate() { return this.delegate; } @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { - return this.delegate.handle(request, response); + public Mono handle(WebServerExchange exchange) { + return this.delegate.handle(exchange); } @Override diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/InternalServerErrorExceptionHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebServerExchange.java similarity index 60% rename from spring-web-reactive/src/main/java/org/springframework/web/server/InternalServerErrorExceptionHandler.java rename to spring-web-reactive/src/main/java/org/springframework/web/server/WebServerExchange.java index a5580d794a6..87fbf7ac3c1 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/InternalServerErrorExceptionHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebServerExchange.java @@ -15,24 +15,33 @@ */ package org.springframework.web.server; -import reactor.Mono; +import java.util.Map; -import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; /** - * Handle any exception by setting the response status to 500. + * Contract for an HTTP request-response interaction. Provides access to the HTTP + * request and response and also exposes additional server-side processing + * related properties and features such as request attributes. * * @author Rossen Stoyanchev */ -public class InternalServerErrorExceptionHandler implements HttpExceptionHandler { +public interface WebServerExchange { + /** + * @return the current HTTP request + */ + ServerHttpRequest getRequest(); - @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) { - response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); - return Mono.empty(); - } + /** + * @return the current HTTP response + */ + ServerHttpResponse getResponse(); + + /** + * @return mutable map of request attributes for the current exchange + */ + Map getAttributes(); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/HttpExceptionHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebToHttpHandlerAdapter.java similarity index 54% rename from spring-web-reactive/src/main/java/org/springframework/web/server/HttpExceptionHandler.java rename to spring-web-reactive/src/main/java/org/springframework/web/server/WebToHttpHandlerAdapter.java index 2d0dc801135..b2d45e7b6f4 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/HttpExceptionHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebToHttpHandlerAdapter.java @@ -22,26 +22,26 @@ import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; /** - * A contract for resolving exceptions from HTTP request handling. - * - *

{@link ErrorHandlingHttpHandler} provides a way of applying a list - * {@link HttpExceptionHandler}s to a target {@link HttpHandler}. + * Adapt {@link WebHandler} to {@link HttpHandler} also creating the + * {@link WebServerExchange} before invoking the target {@code WebHandler}. * * @author Rossen Stoyanchev - * @see ErrorHandlingHttpHandler */ -public interface HttpExceptionHandler { +public class WebToHttpHandlerAdapter extends WebHandlerDecorator implements HttpHandler { - /** - * Handle the given exception and return a completion Publisher to indicate - * when error handling is complete, or send an error signal if the exception - * was not handled. - * - * @param request the current request - * @param response the current response - * @param ex the exception to handle - * @return {@code Mono} to indicate when exception handling is complete. - */ - Mono handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex); + + public WebToHttpHandlerAdapter(WebHandler delegate) { + super(delegate); + } + + @Override + public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { + WebServerExchange exchange = createWebServerExchange(request, response); + return getDelegate().handle(exchange); + } + + protected WebServerExchange createWebServerExchange(ServerHttpRequest request, ServerHttpResponse response) { + return new DefaultWebServerExchange(request, response); + } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/WebToHttpHandlerBuilder.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebToHttpHandlerBuilder.java new file mode 100644 index 00000000000..62a133a35e0 --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebToHttpHandlerBuilder.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.server; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +/** + * Assist with building an + * {@link org.springframework.http.server.reactive.HttpHandler HttpHandler} to + * invoke a target {@link WebHandler} with an optional chain of + * {@link WebFilter}s and one or more {@link WebExceptionHandler}s. + * + *

Effective this sets up the following {@code WebHandler} delegation:
+ * {@link WebToHttpHandlerAdapter} {@code -->} + * {@link ExceptionHandlingWebHandler} {@code -->} + * {@link FilteringWebHandler} + * + * @author Rossen Stoyanchev + */ +public class WebToHttpHandlerBuilder { + + private final WebHandler targetHandler; + + private final List filters = new ArrayList<>(); + + private final List exceptionHandlers = new ArrayList<>(); + + + private WebToHttpHandlerBuilder(WebHandler targetHandler) { + Assert.notNull(targetHandler, "'targetHandler' must not be null"); + this.targetHandler = targetHandler; + } + + + public static WebToHttpHandlerBuilder webHandler(WebHandler webHandler) { + return new WebToHttpHandlerBuilder(webHandler); + } + + public WebToHttpHandlerBuilder filters(WebFilter... filters) { + if (!ObjectUtils.isEmpty(filters)) { + this.filters.addAll(Arrays.asList(filters)); + } + return this; + } + + public WebToHttpHandlerBuilder exceptionHandlers(WebExceptionHandler... exceptionHandlers) { + if (!ObjectUtils.isEmpty(exceptionHandlers)) { + this.exceptionHandlers.addAll(Arrays.asList(exceptionHandlers)); + } + return this; + } + + public WebToHttpHandlerAdapter build() { + WebHandler webHandler = this.targetHandler; + if (!this.exceptionHandlers.isEmpty()) { + WebExceptionHandler[] array = new WebExceptionHandler[this.exceptionHandlers.size()]; + webHandler = new ExceptionHandlingWebHandler(webHandler, this.exceptionHandlers.toArray(array)); + } + if (!this.filters.isEmpty()) { + WebFilter[] array = new WebFilter[this.filters.size()]; + webHandler = new FilteringWebHandler(webHandler, this.filters.toArray(array)); + } + return new WebToHttpHandlerAdapter(webHandler); + } + +} diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java index 5f7c3e3197b..216fb15c2ed 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java @@ -24,7 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.reactivestreams.Publisher; import reactor.Mono; -import reactor.rx.Streams; +import reactor.rx.Stream; import reactor.rx.stream.Signal; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -36,25 +36,25 @@ import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.web.ResponseStatusException; -import org.springframework.web.server.ErrorHandlingHttpHandler; -import org.springframework.web.server.FilterChainHttpHandler; -import org.springframework.web.server.HttpExceptionHandler; -import org.springframework.web.server.HttpFilter; -import org.springframework.web.server.HttpFilterChain; -import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpResponse; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Controller; import org.springframework.web.HttpMediaTypeNotAcceptableException; +import org.springframework.web.ResponseStatusException; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.reactive.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.reactive.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.reactive.method.annotation.ResponseBodyResultHandler; +import org.springframework.web.server.DefaultWebServerExchange; +import org.springframework.web.server.ExceptionHandlingWebHandler; +import org.springframework.web.server.FilteringWebHandler; +import org.springframework.web.server.WebExceptionHandler; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import org.springframework.web.server.WebHandler; +import org.springframework.web.server.WebServerExchange; import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertEquals; @@ -80,6 +80,8 @@ public class DispatcherHandlerErrorTests { private MockServerHttpResponse response; + private WebServerExchange exchange; + @Before public void setUp() throws Exception { @@ -92,6 +94,7 @@ public class DispatcherHandlerErrorTests { this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("/")); this.response = new MockServerHttpResponse(); + this.exchange = new DefaultWebServerExchange(this.request, this.response); } @@ -99,7 +102,7 @@ public class DispatcherHandlerErrorTests { public void noHandler() throws Exception { this.request.setUri(new URI("/does-not-exist")); - Publisher publisher = this.dispatcherHandler.handle(this.request, this.response); + Publisher publisher = this.dispatcherHandler.handle(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(ResponseStatusException.class, ex.getClass()); @@ -111,7 +114,7 @@ public class DispatcherHandlerErrorTests { public void noResolverForArgument() throws Exception { this.request.setUri(new URI("/uknown-argument-type")); - Publisher publisher = this.dispatcherHandler.handle(this.request, this.response); + Publisher publisher = this.dispatcherHandler.handle(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(IllegalStateException.class, ex.getClass()); @@ -122,7 +125,7 @@ public class DispatcherHandlerErrorTests { public void controllerMethodError() throws Exception { this.request.setUri(new URI("/error-signal")); - Publisher publisher = this.dispatcherHandler.handle(this.request, this.response); + Publisher publisher = this.dispatcherHandler.handle(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertSame(EXCEPTION, ex); @@ -132,7 +135,7 @@ public class DispatcherHandlerErrorTests { public void controllerMethodWithThrownException() throws Exception { this.request.setUri(new URI("/raise-exception")); - Publisher publisher = this.dispatcherHandler.handle(this.request, this.response); + Publisher publisher = this.dispatcherHandler.handle(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertSame(EXCEPTION, ex); @@ -142,7 +145,7 @@ public class DispatcherHandlerErrorTests { public void noHandlerResultHandler() throws Exception { this.request.setUri(new URI("/unknown-return-type")); - Publisher publisher = this.dispatcherHandler.handle(this.request, this.response); + Publisher publisher = this.dispatcherHandler.handle(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(IllegalStateException.class, ex.getClass()); @@ -155,7 +158,7 @@ public class DispatcherHandlerErrorTests { this.request.getHeaders().setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); this.request.setBody(Mono.just(ByteBuffer.wrap("body".getBytes("UTF-8")))); - Publisher publisher = this.dispatcherHandler.handle(this.request, this.response); + Publisher publisher = this.dispatcherHandler.handle(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(ResponseStatusException.class, ex.getClass()); @@ -168,7 +171,7 @@ public class DispatcherHandlerErrorTests { this.request.setUri(new URI("/request-body")); this.request.setBody(Mono.error(EXCEPTION)); - Publisher publisher = this.dispatcherHandler.handle(this.request, this.response); + Publisher publisher = this.dispatcherHandler.handle(this.exchange); Throwable ex = awaitErrorSignal(publisher); ex.printStackTrace(); @@ -180,11 +183,11 @@ public class DispatcherHandlerErrorTests { public void dispatcherHandlerWithHttpExceptionHandler() throws Exception { this.request.setUri(new URI("/uknown-argument-type")); - HttpExceptionHandler exceptionHandler = new ServerError500ExceptionHandler(); - HttpHandler httpHandler = new ErrorHandlingHttpHandler(this.dispatcherHandler, exceptionHandler); - Publisher publisher = httpHandler.handle(this.request, this.response); + WebExceptionHandler exceptionHandler = new ServerError500ExceptionHandler(); + WebHandler webHandler = new ExceptionHandlingWebHandler(this.dispatcherHandler, exceptionHandler); + Publisher publisher = webHandler.handle(this.exchange); - Streams.from(publisher).toList().get(); + Stream.from(publisher).toList().get(); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); } @@ -192,18 +195,17 @@ public class DispatcherHandlerErrorTests { public void filterChainWithHttpExceptionHandler() throws Exception { this.request.setUri(new URI("/uknown-argument-type")); - HttpHandler httpHandler; - httpHandler = new FilterChainHttpHandler(this.dispatcherHandler, new TestHttpFilter()); - httpHandler = new ErrorHandlingHttpHandler(httpHandler, new ServerError500ExceptionHandler()); - Publisher publisher = httpHandler.handle(this.request, this.response); + WebHandler webHandler = new FilteringWebHandler(this.dispatcherHandler, new TestWebFilter()); + webHandler = new ExceptionHandlingWebHandler(webHandler, new ServerError500ExceptionHandler()); + Publisher publisher = webHandler.handle(this.exchange); - Streams.from(publisher).toList().get(); + Stream.from(publisher).toList().get(); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); } private Throwable awaitErrorSignal(Publisher publisher) throws Exception { - Signal signal = Streams.from(publisher).materialize().toList().get().get(0); + Signal signal = Stream.from(publisher).materialize().toList().get().get(0); assertEquals("Unexpected signal: " + signal, Signal.Type.ERROR, signal.getType()); return signal.getThrowable(); } @@ -269,20 +271,20 @@ public class DispatcherHandlerErrorTests { private static class Foo { } - private static class ServerError500ExceptionHandler implements HttpExceptionHandler { + private static class ServerError500ExceptionHandler implements WebExceptionHandler { @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) { - response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + public Mono handle(WebServerExchange exchange, Throwable ex) { + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); return Mono.empty(); } } - private static class TestHttpFilter implements HttpFilter { + private static class TestWebFilter implements WebFilter { @Override - public Mono filter(ServerHttpRequest req, ServerHttpResponse res, HttpFilterChain chain) { - return chain.filter(req, res); + public Mono filter(WebServerExchange exchange, WebFilterChain chain) { + return chain.filter(exchange); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/ResponseStatusExceptionHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/ResponseStatusExceptionHandlerTests.java index 3744ecaa6ee..68a2ce3c721 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/ResponseStatusExceptionHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/ResponseStatusExceptionHandlerTests.java @@ -17,12 +17,11 @@ package org.springframework.web.reactive; import java.net.URI; import java.util.List; -import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import org.reactivestreams.Publisher; -import reactor.rx.Streams; +import reactor.rx.Stream; import reactor.rx.stream.Signal; import org.springframework.http.HttpMethod; @@ -30,6 +29,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.web.ResponseStatusException; +import org.springframework.web.server.DefaultWebServerExchange; +import org.springframework.web.server.WebServerExchange; import static junit.framework.TestCase.assertSame; import static org.junit.Assert.assertEquals; @@ -42,34 +43,35 @@ public class ResponseStatusExceptionHandlerTests { private ResponseStatusExceptionHandler handler; - private MockServerHttpRequest request; - private MockServerHttpResponse response; + private WebServerExchange exchange; + @Before public void setUp() throws Exception { this.handler = new ResponseStatusExceptionHandler(); - this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("/path")); + MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/path")); this.response = new MockServerHttpResponse(); + this.exchange = new DefaultWebServerExchange(request, this.response); } @Test public void handleException() throws Exception { Throwable ex = new ResponseStatusException(HttpStatus.BAD_REQUEST); - Publisher publisher = this.handler.handle(this.request, this.response, ex); + Publisher publisher = this.handler.handle(this.exchange, ex); - Streams.from(publisher).toList().get(); + Stream.from(publisher).toList().get(); assertEquals(HttpStatus.BAD_REQUEST, this.response.getStatus()); } @Test public void unresolvedException() throws Exception { Throwable ex = new IllegalStateException(); - Publisher publisher = this.handler.handle(this.request, this.response, ex); + Publisher publisher = this.handler.handle(this.exchange, ex); - List> signals = Streams.from(publisher).materialize().toList().get(); + List> signals = Stream.from(publisher).materialize().toList().get(); assertEquals(1, signals.size()); assertTrue(signals.get(0).hasError()); assertSame(ex, signals.get(0).getThrowable()); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMappingIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMappingIntegrationTests.java index 3507f005f4e..8bd43ba8309 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMappingIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMappingIntegrationTests.java @@ -31,7 +31,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests; -import org.springframework.web.server.ErrorHandlingHttpHandler; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -39,6 +38,9 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.ResponseStatusExceptionHandler; +import org.springframework.web.server.WebHandler; +import org.springframework.web.server.WebServerExchange; +import org.springframework.web.server.WebToHttpHandlerBuilder; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -57,13 +59,16 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler StaticApplicationContext wac = new StaticApplicationContext(); wac.registerSingleton("hm", TestHandlerMapping.class); - wac.registerSingleton("ha", HttpHandlerAdapter.class); + wac.registerSingleton("ha", HttpHandlerHandlerAdapter.class); wac.registerSingleton("rh", SimpleHandlerResultHandler.class); wac.refresh(); - DispatcherHandler dispatcherHandler = new DispatcherHandler(); - dispatcherHandler.setApplicationContext(wac); - return new ErrorHandlingHttpHandler(dispatcherHandler, new ResponseStatusExceptionHandler()); + DispatcherHandler webHandler = new DispatcherHandler(); + webHandler.setApplicationContext(wac); + + return WebToHttpHandlerBuilder.webHandler(webHandler) + .exceptionHandlers(new ResponseStatusExceptionHandler()) + .build(); } @Test @@ -132,27 +137,27 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler } } - private static class FooHandler implements HttpHandler { + private static class FooHandler implements WebHandler { @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { - return response.setBody(Stream.just(Buffer.wrap("foo").byteBuffer())); + public Mono handle(WebServerExchange exchange) { + return exchange.getResponse().setBody(Stream.just(Buffer.wrap("foo").byteBuffer())); } } - private static class BarHandler implements HttpHandler { + private static class BarHandler implements WebHandler { @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { - return response.setBody(Stream.just(Buffer.wrap("bar").byteBuffer())); + public Mono handle(WebServerExchange exchange) { + return exchange.getResponse().setBody(Stream.just(Buffer.wrap("bar").byteBuffer())); } } - private static class HeaderSettingHandler implements HttpHandler { + private static class HeaderSettingHandler implements WebHandler { @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { - response.getHeaders().add("foo", "bar"); + public Mono handle(WebServerExchange exchange) { + exchange.getResponse().getHeaders().add("foo", "bar"); return Mono.empty(); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/InvocableHandlerMethodTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/InvocableHandlerMethodTests.java index 65a31fbd3ab..fef38f5552b 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/InvocableHandlerMethodTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/InvocableHandlerMethodTests.java @@ -27,14 +27,17 @@ import org.junit.Test; import org.reactivestreams.Publisher; import reactor.Flux; import reactor.Mono; -import reactor.rx.Streams; +import reactor.rx.Stream; import reactor.rx.stream.Signal; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.method.annotation.RequestParamArgumentResolver; +import org.springframework.web.server.DefaultWebServerExchange; +import org.springframework.web.server.WebServerExchange; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; @@ -49,10 +52,13 @@ public class InvocableHandlerMethodTests { private ServerHttpRequest request; + private WebServerExchange exchange; + @Before public void setUp() throws Exception { this.request = mock(ServerHttpRequest.class); + this.exchange = new DefaultWebServerExchange(request, mock(ServerHttpResponse.class)); } @@ -60,8 +66,8 @@ public class InvocableHandlerMethodTests { public void noArgsMethod() throws Exception { InvocableHandlerMethod hm = createHandlerMethod("noArgs"); - Publisher publisher = hm.invokeForRequest(this.request); - List values = Streams.from(publisher).toList().get(); + Publisher publisher = hm.invokeForRequest(this.exchange); + List values = Stream.from(publisher).toList().get(); assertEquals(1, values.size()); assertEquals("success", values.get(0).getResult()); @@ -73,8 +79,8 @@ public class InvocableHandlerMethodTests { InvocableHandlerMethod hm = createHandlerMethod("singleArg", String.class); hm.setHandlerMethodArgumentResolvers(Collections.singletonList(new RequestParamArgumentResolver())); - Publisher publisher = hm.invokeForRequest(this.request); - List values = Streams.from(publisher).toList().get(); + Publisher publisher = hm.invokeForRequest(this.exchange); + List values = Stream.from(publisher).toList().get(); assertEquals(1, values.size()); assertEquals("success:null", values.get(0).getResult()); @@ -85,8 +91,8 @@ public class InvocableHandlerMethodTests { InvocableHandlerMethod hm = createHandlerMethod("singleArg", String.class); addResolver(hm, Mono.just("value1")); - Publisher publisher = hm.invokeForRequest(this.request); - List values = Streams.from(publisher).toList().get(); + Publisher publisher = hm.invokeForRequest(this.exchange); + List values = Stream.from(publisher).toList().get(); assertEquals(1, values.size()); assertEquals("success:value1", values.get(0).getResult()); @@ -97,8 +103,8 @@ public class InvocableHandlerMethodTests { InvocableHandlerMethod hm = createHandlerMethod("singleArg", String.class); addResolver(hm, Flux.fromIterable(Arrays.asList("value1", "value2", "value3"))); - Publisher publisher = hm.invokeForRequest(this.request); - List values = Streams.from(publisher).toList().get(); + Publisher publisher = hm.invokeForRequest(this.exchange); + List values = Stream.from(publisher).toList().get(); assertEquals(1, values.size()); assertEquals("success:value1", values.get(0).getResult()); @@ -108,7 +114,7 @@ public class InvocableHandlerMethodTests { public void noResolverForArg() throws Exception { InvocableHandlerMethod hm = createHandlerMethod("singleArg", String.class); - Publisher publisher = hm.invokeForRequest(this.request); + Publisher publisher = hm.invokeForRequest(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(IllegalStateException.class, ex.getClass()); @@ -125,7 +131,7 @@ public class InvocableHandlerMethodTests { InvocableHandlerMethod hm = createHandlerMethod("singleArg", String.class); hm.setHandlerMethodArgumentResolvers(Collections.singletonList(resolver)); - Publisher publisher = hm.invokeForRequest(this.request); + Publisher publisher = hm.invokeForRequest(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(IllegalStateException.class, ex.getClass()); @@ -139,7 +145,7 @@ public class InvocableHandlerMethodTests { InvocableHandlerMethod hm = createHandlerMethod("singleArg", String.class); addResolver(hm, Mono.error(new IllegalStateException("boo"))); - Publisher publisher = hm.invokeForRequest(this.request); + Publisher publisher = hm.invokeForRequest(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(IllegalStateException.class, ex.getClass()); @@ -153,7 +159,7 @@ public class InvocableHandlerMethodTests { InvocableHandlerMethod hm = createHandlerMethod("singleArg", String.class); addResolver(hm, Mono.just(1)); - Publisher publisher = hm.invokeForRequest(this.request); + Publisher publisher = hm.invokeForRequest(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(IllegalStateException.class, ex.getClass()); @@ -166,7 +172,7 @@ public class InvocableHandlerMethodTests { public void invocationTargetExceptionIsUnwrapped() throws Exception { InvocableHandlerMethod hm = createHandlerMethod("exceptionMethod"); - Publisher publisher = hm.invokeForRequest(this.request); + Publisher publisher = hm.invokeForRequest(this.exchange); Throwable ex = awaitErrorSignal(publisher); assertEquals(IllegalStateException.class, ex.getClass()); @@ -188,7 +194,7 @@ public class InvocableHandlerMethodTests { } private Throwable awaitErrorSignal(Publisher publisher) throws Exception { - Signal signal = Streams.from(publisher).materialize().toList().get().get(0); + Signal signal = Stream.from(publisher).materialize().toList().get().get(0); assertEquals("Unexpected signal: " + signal, Signal.Type.ERROR, signal.getType()); return signal.getThrowable(); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMappingTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMappingTests.java index 0af8d8eadce..945c2b6ff23 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMappingTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMappingTests.java @@ -22,16 +22,19 @@ import java.util.List; import org.junit.Before; import org.junit.Test; import org.reactivestreams.Publisher; -import reactor.rx.Streams; +import reactor.rx.Stream; import org.springframework.context.support.StaticApplicationContext; import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.MockServerHttpRequest; +import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.method.HandlerMethod; +import org.springframework.web.server.DefaultWebServerExchange; +import org.springframework.web.server.WebServerExchange; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -57,7 +60,8 @@ public class RequestMappingHandlerMappingTests { @Test public void path() throws Exception { ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("boo")); - Publisher handlerPublisher = this.mapping.getHandler(request); + WebServerExchange exchange = new DefaultWebServerExchange(request, new MockServerHttpResponse()); + Publisher handlerPublisher = this.mapping.getHandler(exchange); HandlerMethod handlerMethod = toHandlerMethod(handlerPublisher); assertEquals(TestController.class.getMethod("boo"), handlerMethod.getMethod()); } @@ -65,19 +69,21 @@ public class RequestMappingHandlerMappingTests { @Test public void method() throws Exception { ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.POST, new URI("foo")); - Publisher handlerPublisher = this.mapping.getHandler(request); + WebServerExchange exchange = new DefaultWebServerExchange(request, new MockServerHttpResponse()); + Publisher handlerPublisher = this.mapping.getHandler(exchange); HandlerMethod handlerMethod = toHandlerMethod(handlerPublisher); assertEquals(TestController.class.getMethod("postFoo"), handlerMethod.getMethod()); request = new MockServerHttpRequest(HttpMethod.GET, new URI("foo")); - handlerPublisher = this.mapping.getHandler(request); + exchange = new DefaultWebServerExchange(request, new MockServerHttpResponse()); + handlerPublisher = this.mapping.getHandler(exchange); handlerMethod = toHandlerMethod(handlerPublisher); assertEquals(TestController.class.getMethod("getFoo"), handlerMethod.getMethod()); } private HandlerMethod toHandlerMethod(Publisher handlerPublisher) throws InterruptedException { assertNotNull(handlerPublisher); - List handlerList = Streams.from(handlerPublisher).toList().get(); + List handlerList = Stream.from(handlerPublisher).toList().get(); assertEquals(1, handlerList.size()); return (HandlerMethod) handlerList.get(0); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/annotation/RequestMappingIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/annotation/RequestMappingIntegrationTests.java index 5c82eadaf45..11756412129 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/annotation/RequestMappingIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/method/annotation/RequestMappingIntegrationTests.java @@ -61,6 +61,8 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.handler.SimpleHandlerResultHandler; +import org.springframework.web.server.WebToHttpHandlerAdapter; +import org.springframework.web.server.WebToHttpHandlerBuilder; import static org.junit.Assert.assertEquals; @@ -80,9 +82,10 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati this.wac.register(FrameworkConfig.class, ApplicationConfig.class); this.wac.refresh(); - DispatcherHandler dispatcherHandler = new DispatcherHandler(); - dispatcherHandler.setApplicationContext(this.wac); - return dispatcherHandler; + DispatcherHandler webHandler = new DispatcherHandler(); + webHandler.setApplicationContext(this.wac); + + return WebToHttpHandlerBuilder.webHandler(webHandler).build(); } @Test diff --git a/spring-web-reactive/src/test/java/org/springframework/web/server/ErrorHandlingHttpHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/server/ErrorHandlingHttpHandlerTests.java deleted file mode 100644 index 43b431c90d6..00000000000 --- a/spring-web-reactive/src/test/java/org/springframework/web/server/ErrorHandlingHttpHandlerTests.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2002-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.web.server; - - -import java.net.URI; - -import org.junit.Before; -import org.junit.Test; -import org.reactivestreams.Publisher; -import reactor.Mono; -import reactor.rx.Streams; -import reactor.rx.stream.Signal; - -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.http.server.reactive.MockServerHttpRequest; -import org.springframework.http.server.reactive.MockServerHttpResponse; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.web.server.ErrorHandlingHttpHandler; -import org.springframework.web.server.HttpExceptionHandler; -import org.springframework.web.server.InternalServerErrorExceptionHandler; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -/** - * @author Rossen Stoyanchev - */ -@SuppressWarnings("ThrowableResultOfMethodCallIgnored") -public class ErrorHandlingHttpHandlerTests { - - private MockServerHttpRequest request; - - private MockServerHttpResponse response; - - - @Before - public void setUp() throws Exception { - this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("http://localhost:8080")); - this.response = new MockServerHttpResponse(); - } - - - @Test - public void handleErrorSignal() throws Exception { - HttpExceptionHandler exceptionHandler = new InternalServerErrorExceptionHandler(); - HttpHandler targetHandler = new TestHttpHandler(new IllegalStateException("boo")); - HttpHandler handler = new ErrorHandlingHttpHandler(targetHandler, exceptionHandler); - - handler.handle(this.request, this.response).get(); - - assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); - } - - @Test - public void handleErrorSignalWithMultipleHttpErrorHandlers() throws Exception { - HttpExceptionHandler[] exceptionHandlers = new HttpExceptionHandler[] { - new UnresolvedExceptionHandler(), - new UnresolvedExceptionHandler(), - new InternalServerErrorExceptionHandler(), - new UnresolvedExceptionHandler() - }; - HttpHandler targetHandler = new TestHttpHandler(new IllegalStateException("boo")); - HttpHandler httpHandler = new ErrorHandlingHttpHandler(targetHandler, exceptionHandlers); - - httpHandler.handle(this.request, this.response).get(); - - assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); - } - - @Test - public void unresolvedException() throws Exception { - HttpExceptionHandler exceptionHandler = new UnresolvedExceptionHandler(); - HttpHandler targetHandler = new TestHttpHandler(new IllegalStateException("boo")); - HttpHandler httpHandler = new ErrorHandlingHttpHandler(targetHandler, exceptionHandler); - - Publisher publisher = httpHandler.handle(this.request, this.response); - Throwable ex = awaitErrorSignal(publisher); - - assertEquals("boo", ex.getMessage()); - assertNull(this.response.getStatus()); - } - - @Test - public void thrownExceptionBecomesErrorSignal() throws Exception { - HttpExceptionHandler exceptionHandler = new InternalServerErrorExceptionHandler(); - HttpHandler targetHandler = new TestHttpHandler(new IllegalStateException("boo"), true); - HttpHandler handler = new ErrorHandlingHttpHandler(targetHandler, exceptionHandler); - - handler.handle(this.request, this.response).get(); - - assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); - } - - - private Throwable awaitErrorSignal(Publisher publisher) throws Exception { - Signal signal = Streams.from(publisher).materialize().toList().get().get(0); - assertEquals("Unexpected signal: " + signal, Signal.Type.ERROR, signal.getType()); - return signal.getThrowable(); - } - - - private static class TestHttpHandler implements HttpHandler { - - private final RuntimeException exception; - - private final boolean raise; - - - public TestHttpHandler(RuntimeException exception) { - this(exception, false); - } - - public TestHttpHandler(RuntimeException exception, boolean raise) { - this.exception = exception; - this.raise = raise; - } - - @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { - if (this.raise) { - throw this.exception; - } - return Mono.error(this.exception); - } - } - - - /** Leave the exception unresolved. */ - private static class UnresolvedExceptionHandler implements HttpExceptionHandler { - - @Override - public Mono handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) { - return Mono.error(ex); - } - } - -} diff --git a/spring-web-reactive/src/test/java/org/springframework/web/server/ExceptionHandlingHttpHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/server/ExceptionHandlingHttpHandlerTests.java new file mode 100644 index 00000000000..48cf44bdd00 --- /dev/null +++ b/spring-web-reactive/src/test/java/org/springframework/web/server/ExceptionHandlingHttpHandlerTests.java @@ -0,0 +1,140 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.server; + + +import java.net.URI; + +import org.junit.Before; +import org.junit.Test; +import reactor.Mono; + +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.MockServerHttpRequest; +import org.springframework.http.server.reactive.MockServerHttpResponse; + +import static org.junit.Assert.assertEquals; + +/** + * @author Rossen Stoyanchev + */ +@SuppressWarnings("ThrowableResultOfMethodCallIgnored") +public class ExceptionHandlingHttpHandlerTests { + + private MockServerHttpResponse response; + + private WebServerExchange exchange; + + private WebHandler targetHandler; + + + @Before + public void setUp() throws Exception { + URI uri = new URI("http://localhost:8080"); + MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, uri); + this.response = new MockServerHttpResponse(); + this.exchange = new DefaultWebServerExchange(request, this.response); + this.targetHandler = new StubWebHandler(new IllegalStateException("boo")); + } + + + @Test + public void handleErrorSignal() throws Exception { + WebExceptionHandler exceptionHandler = new BadRequestExceptionHandler(); + createWebHandler(exceptionHandler).handle(this.exchange).get(); + + assertEquals(HttpStatus.BAD_REQUEST, this.response.getStatus()); + } + + @Test + public void handleErrorSignalWithMultipleHttpErrorHandlers() throws Exception { + WebExceptionHandler[] exceptionHandlers = new WebExceptionHandler[] { + new UnresolvedExceptionHandler(), + new UnresolvedExceptionHandler(), + new BadRequestExceptionHandler(), + new UnresolvedExceptionHandler() + }; + createWebHandler(exceptionHandlers).handle(this.exchange).get(); + + assertEquals(HttpStatus.BAD_REQUEST, this.response.getStatus()); + } + + @Test + public void unresolvedException() throws Exception { + WebExceptionHandler exceptionHandler = new UnresolvedExceptionHandler(); + createWebHandler(exceptionHandler).handle(this.exchange).get(); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); + } + + @Test + public void thrownExceptionBecomesErrorSignal() throws Exception { + WebExceptionHandler exceptionHandler = new BadRequestExceptionHandler(); + createWebHandler(exceptionHandler).handle(this.exchange).get(); + + assertEquals(HttpStatus.BAD_REQUEST, this.response.getStatus()); + } + + private WebHandler createWebHandler(WebExceptionHandler... handlers) { + return new ExceptionHandlingWebHandler(this.targetHandler, handlers); + } + + + private static class StubWebHandler implements WebHandler { + + private final RuntimeException exception; + + private final boolean raise; + + + public StubWebHandler(RuntimeException exception) { + this(exception, false); + } + + public StubWebHandler(RuntimeException exception, boolean raise) { + this.exception = exception; + this.raise = raise; + } + + @Override + public Mono handle(WebServerExchange exchange) { + if (this.raise) { + throw this.exception; + } + return Mono.error(this.exception); + } + } + + private static class BadRequestExceptionHandler implements WebExceptionHandler { + + @Override + public Mono handle(WebServerExchange exchange, Throwable ex) { + exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST); + return Mono.empty(); + } + } + + /** Leave the exception unresolved. */ + private static class UnresolvedExceptionHandler implements WebExceptionHandler { + + @Override + public Mono handle(WebServerExchange exchange, Throwable ex) { + return Mono.error(ex); + } + } + +} diff --git a/spring-web-reactive/src/test/java/org/springframework/web/server/FilterChainHttpHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/server/FilteringWebHandlerTests.java similarity index 61% rename from spring-web-reactive/src/test/java/org/springframework/web/server/FilterChainHttpHandlerTests.java rename to spring-web-reactive/src/test/java/org/springframework/web/server/FilteringWebHandlerTests.java index 370dd644db3..e06a9104a2b 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/server/FilterChainHttpHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/server/FilteringWebHandlerTests.java @@ -33,9 +33,9 @@ import static org.mockito.Mockito.mock; /** * @author Rossen Stoyanchev */ -public class FilterChainHttpHandlerTests { +public class FilteringWebHandlerTests { - private static Log logger = LogFactory.getLog(FilterChainHttpHandlerTests.class); + private static Log logger = LogFactory.getLog(FilteringWebHandlerTests.class); private ServerHttpRequest request; @@ -51,61 +51,60 @@ public class FilterChainHttpHandlerTests { @Test public void multipleFilters() throws Exception { - StubHandler handler = new StubHandler(); + StubWebHandler webHandler = new StubWebHandler(); TestFilter filter1 = new TestFilter(); TestFilter filter2 = new TestFilter(); TestFilter filter3 = new TestFilter(); - FilterChainHttpHandler filterHandler = new FilterChainHttpHandler(handler, filter1, filter2, filter3); - - filterHandler.handle(this.request, this.response).get(); + HttpHandler httpHandler = createHttpHandler(webHandler, filter1, filter2, filter3); + httpHandler.handle(this.request, this.response).get(); assertTrue(filter1.invoked()); assertTrue(filter2.invoked()); assertTrue(filter3.invoked()); - assertTrue(handler.invoked()); + assertTrue(webHandler.invoked()); } @Test public void zeroFilters() throws Exception { - StubHandler handler = new StubHandler(); - FilterChainHttpHandler filterHandler = new FilterChainHttpHandler(handler); + StubWebHandler webHandler = new StubWebHandler(); + HttpHandler httpHandler = createHttpHandler(webHandler); + httpHandler.handle(this.request, this.response).get(); - filterHandler.handle(this.request, this.response).get(); - - assertTrue(handler.invoked()); + assertTrue(webHandler.invoked()); } @Test public void shortcircuitFilter() throws Exception { - StubHandler handler = new StubHandler(); + StubWebHandler webHandler = new StubWebHandler(); TestFilter filter1 = new TestFilter(); ShortcircuitingFilter filter2 = new ShortcircuitingFilter(); TestFilter filter3 = new TestFilter(); - FilterChainHttpHandler filterHandler = new FilterChainHttpHandler(handler, filter1, filter2, filter3); - - filterHandler.handle(this.request, this.response).get(); + HttpHandler httpHandler = createHttpHandler(webHandler, filter1, filter2, filter3); + httpHandler.handle(this.request, this.response).get(); assertTrue(filter1.invoked()); assertTrue(filter2.invoked()); assertFalse(filter3.invoked()); - assertFalse(handler.invoked()); + assertFalse(webHandler.invoked()); } @Test public void asyncFilter() throws Exception { - StubHandler handler = new StubHandler(); + StubWebHandler webHandler = new StubWebHandler(); AsyncFilter filter = new AsyncFilter(); - FilterChainHttpHandler filterHandler = new FilterChainHttpHandler(handler, filter); - - filterHandler.handle(this.request, this.response).get(); + HttpHandler httpHandler = createHttpHandler(webHandler, filter); + httpHandler.handle(this.request, this.response).get(); assertTrue(filter.invoked()); - assertTrue(handler.invoked()); + assertTrue(webHandler.invoked()); + } + + private WebToHttpHandlerAdapter createHttpHandler(StubWebHandler webHandler, WebFilter... filters) { + return WebToHttpHandlerBuilder.webHandler(webHandler).filters(filters).build(); } - - private static class TestFilter implements HttpFilter { + private static class TestFilter implements WebFilter { private volatile boolean invoked; @@ -115,26 +114,20 @@ public class FilterChainHttpHandlerTests { } @Override - public Mono filter(ServerHttpRequest req, ServerHttpResponse res, - HttpFilterChain chain) { - + public Mono filter(WebServerExchange exchange, WebFilterChain chain) { this.invoked = true; - return doFilter(req, res, chain); + return doFilter(exchange, chain); } - public Mono doFilter(ServerHttpRequest req, ServerHttpResponse res, - HttpFilterChain chain) { - - return chain.filter(req, res); + public Mono doFilter(WebServerExchange exchange, WebFilterChain chain) { + return chain.filter(exchange); } } private static class ShortcircuitingFilter extends TestFilter { @Override - public Mono doFilter(ServerHttpRequest req, ServerHttpResponse res, - HttpFilterChain chain) { - + public Mono doFilter(WebServerExchange exchange, WebFilterChain chain) { return Mono.empty(); } } @@ -142,10 +135,10 @@ public class FilterChainHttpHandlerTests { private static class AsyncFilter extends TestFilter { @Override - public Mono doFilter(ServerHttpRequest req, ServerHttpResponse res, HttpFilterChain chain) { + public Mono doFilter(WebServerExchange exchange, WebFilterChain chain) { return doAsyncWork().then(asyncResult -> { logger.debug("Async result: " + asyncResult); - return chain.filter(req, res); + return chain.filter(exchange); }); } @@ -155,7 +148,7 @@ public class FilterChainHttpHandlerTests { } - private static class StubHandler implements HttpHandler { + private static class StubWebHandler implements WebHandler { private volatile boolean invoked; @@ -164,7 +157,7 @@ public class FilterChainHttpHandlerTests { } @Override - public Mono handle(ServerHttpRequest req, ServerHttpResponse res) { + public Mono handle(WebServerExchange exchange) { logger.trace("StubHandler invoked."); this.invoked = true; return Mono.empty();