Add WebServerExchange
This change adds a WebServerExchange and updates all contracts at the the same level (i.e. org.springframework.web.server) as well as the org.springframework.web.reactive level to use it so that all framework-related code will have access to server-side processing features such as request attributes (and others to come).
This commit is contained in:
parent
54e4e012b2
commit
4f614fa0fd
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
public Mono<Void> 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<HandlerResult> invokeHandler(ServerHttpRequest request, ServerHttpResponse response, Object handler) {
|
||||
private Mono<HandlerResult> 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<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response, HandlerResult result) {
|
||||
return getResultHandler(result).handleResult(request, response, result)
|
||||
private Mono<Void> 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<Object> getHandler(ServerHttpRequest request) {
|
||||
public Mono<Object> getHandler(WebServerExchange exchange) {
|
||||
return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response,
|
||||
Object handler);
|
||||
Mono<HandlerResult> handle(WebServerExchange exchange, Object handler);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Object> getHandler(ServerHttpRequest request);
|
||||
Mono<Object> getHandler(WebServerExchange exchange);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Void>} to indicate when request handling is complete.
|
||||
*/
|
||||
Mono<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response,
|
||||
HandlerResult result);
|
||||
Mono<Void> handleResult(WebServerExchange exchange, HandlerResult result);
|
||||
|
||||
}
|
||||
|
|
@ -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<Void> handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) {
|
||||
public Mono<Void> 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);
|
||||
|
|
|
|||
|
|
@ -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<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response, Object handler) {
|
||||
HttpHandler httpHandler = (HttpHandler)handler;
|
||||
Mono<Void> completion = httpHandler.handle(request, response);
|
||||
return Mono.just(new HandlerResult(httpHandler, completion, PUBLISHER_VOID));
|
||||
public Mono<HandlerResult> handle(WebServerExchange exchange, Object handler) {
|
||||
WebHandler webHandler = (WebHandler) handler;
|
||||
Mono<Void> completion = webHandler.handle(exchange);
|
||||
return Mono.just(new HandlerResult(webHandler, completion, PUBLISHER_VOID));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<Void>} value.
|
||||
|
|
@ -75,15 +74,11 @@ public class SimpleHandlerResultHandler implements Ordered, HandlerResultHandler
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Mono<Void> handleResult(ServerHttpRequest request,
|
||||
ServerHttpResponse response, HandlerResult result) {
|
||||
|
||||
public Mono<Void> handleResult(WebServerExchange exchange, HandlerResult result) {
|
||||
Object value = result.getResult();
|
||||
|
||||
if (Void.TYPE.equals(result.getResultType().getRawClass())) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return (value instanceof Mono ? (Mono<Void>)value :
|
||||
Mono.from(this.conversionService.convert(value, Publisher.class)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Object> getHandler(ServerHttpRequest request) {
|
||||
public Mono<Object> 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);
|
||||
|
|
|
|||
|
|
@ -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<Object> resolveArgument(MethodParameter parameter, ServerHttpRequest request);
|
||||
Mono<Object> resolveArgument(MethodParameter parameter, WebServerExchange exchange);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<HandlerResult> invokeForRequest(ServerHttpRequest request, Object... providedArgs) {
|
||||
return resolveArguments(request, providedArgs).then(args -> {
|
||||
public Mono<HandlerResult> 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<Object[]> resolveArguments(ServerHttpRequest request, Object... providedArgs) {
|
||||
private Mono<Object[]> 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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Object> resolveArgument(MethodParameter parameter, ServerHttpRequest request) {
|
||||
MediaType mediaType = request.getHeaders().getContentType();
|
||||
public Mono<Object> 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<ByteBuffer> body = request.getBody();
|
||||
Flux<ByteBuffer> body = exchange.getRequest().getBody();
|
||||
Flux<?> elementFlux = body;
|
||||
ResolvableType elementType = type.hasGenerics() ? type.getGeneric(0) : type;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response,
|
||||
Object handler) {
|
||||
|
||||
public Mono<HandlerResult> 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<HandlerResult> 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()) {
|
||||
|
|
|
|||
|
|
@ -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<Object> getHandler(ServerHttpRequest request) {
|
||||
public Mono<Object> getHandler(WebServerExchange exchange) {
|
||||
return Flux.create(subscriber -> {
|
||||
for (Map.Entry<RequestMappingInfo, HandlerMethod> 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;
|
||||
|
|
|
|||
|
|
@ -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<Object> resolveArgument(MethodParameter param, ServerHttpRequest request) {
|
||||
public Mono<Object> 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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Void> handleResult(ServerHttpRequest request,
|
||||
ServerHttpResponse response, HandlerResult result) {
|
||||
public Mono<Void> 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<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
|
||||
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(exchange.getRequest());
|
||||
List<MediaType> 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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, Object> 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<String, Object> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<HttpExceptionHandler> 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<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
Mono<Void> 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<Void> applyExceptionHandler(Mono<Void> mono, HttpExceptionHandler handler,
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
return mono.otherwise(ex -> handler.handle(request, response, ex)).after();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<WebExceptionHandler> exceptionHandlers;
|
||||
|
||||
|
||||
public ExceptionHandlingWebHandler(WebHandler delegate, WebExceptionHandler... exceptionHandlers) {
|
||||
super(delegate);
|
||||
this.exceptionHandlers = initList(exceptionHandlers);
|
||||
}
|
||||
|
||||
private static List<WebExceptionHandler> 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<WebExceptionHandler> getExceptionHandlers() {
|
||||
return this.exceptionHandlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebServerExchange exchange) {
|
||||
Mono<Void> 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<? extends Void> handleUnresolvedException(WebServerExchange exchange) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<HttpFilter> filters;
|
||||
|
||||
|
||||
public FilterChainHttpHandler(HttpHandler targetHandler, HttpFilter... filters) {
|
||||
super(targetHandler);
|
||||
this.filters = (filters != null ? Arrays.asList(filters) : Collections.emptyList());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return new DefaultHttpFilterChain().filter(request, response);
|
||||
}
|
||||
|
||||
|
||||
private class DefaultHttpFilterChain implements HttpFilterChain {
|
||||
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public Mono<Void> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<WebFilter> filters;
|
||||
|
||||
|
||||
public FilteringWebHandler(WebHandler targetHandler, WebFilter... filters) {
|
||||
super(targetHandler);
|
||||
this.filters = initList(filters);
|
||||
}
|
||||
|
||||
private static List<WebFilter> initList(WebFilter[] list) {
|
||||
return (list != null ? Collections.unmodifiableList(Arrays.asList(list)): Collections.emptyList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return a read-only list of the configured filters.
|
||||
*/
|
||||
public List<WebFilter> getFilters() {
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebServerExchange exchange) {
|
||||
return new DefaultWebFilterChain().filter(exchange);
|
||||
}
|
||||
|
||||
|
||||
private class DefaultWebFilterChain implements WebFilterChain {
|
||||
|
||||
private int index;
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(WebServerExchange exchange) {
|
||||
if (this.index < filters.size()) {
|
||||
WebFilter filter = filters.get(this.index++);
|
||||
return filter.filter(exchange, this);
|
||||
}
|
||||
else {
|
||||
return getDelegate().handle(exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
*
|
||||
* <p>Filters may be used to implement cross-cutting, application-agnostic
|
||||
* requirements such as security, timeouts, and others.
|
||||
*
|
||||
* <p>{@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<Void>} to indicate when request processing is complete.
|
||||
*/
|
||||
Mono<Void> filter(ServerHttpRequest request, ServerHttpResponse response,
|
||||
HttpFilterChain chain);
|
||||
|
||||
}
|
||||
|
|
@ -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<Void>} to indicate when exception handling is complete
|
||||
*/
|
||||
Mono<Void> handle(WebServerExchange exchange, Throwable ex);
|
||||
|
||||
}
|
||||
|
|
@ -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<Void>} to indicate when request processing is complete
|
||||
*/
|
||||
Mono<Void> filter(WebServerExchange exchange, WebFilterChain chain);
|
||||
|
||||
}
|
||||
|
|
@ -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<Void>} to indicate when request handling is complete.
|
||||
* @param exchange the current server exchange
|
||||
* @return {@code Mono<Void>} to indicate when request handling is complete
|
||||
*/
|
||||
Mono<Void> filter(ServerHttpRequest request, ServerHttpResponse response);
|
||||
Mono<Void> filter(WebServerExchange exchange);
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
*
|
||||
* <p>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<Void>} to indicate when request handling is complete
|
||||
*/
|
||||
Mono<Void> handle(WebServerExchange exchange);
|
||||
|
||||
}
|
||||
|
|
@ -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<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return this.delegate.handle(request, response);
|
||||
public Mono<Void> handle(WebServerExchange exchange) {
|
||||
return this.delegate.handle(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -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<Void> 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<String, Object> getAttributes();
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
*
|
||||
* <p>{@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<Void>} to indicate when exception handling is complete.
|
||||
*/
|
||||
Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex);
|
||||
|
||||
public WebToHttpHandlerAdapter(WebHandler delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
*
|
||||
* <p>Effective this sets up the following {@code WebHandler} delegation:<br>
|
||||
* {@link WebToHttpHandlerAdapter} {@code -->}
|
||||
* {@link ExceptionHandlingWebHandler} {@code -->}
|
||||
* {@link FilteringWebHandler}
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class WebToHttpHandlerBuilder {
|
||||
|
||||
private final WebHandler targetHandler;
|
||||
|
||||
private final List<WebFilter> filters = new ArrayList<>();
|
||||
|
||||
private final List<WebExceptionHandler> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<Void> publisher = this.dispatcherHandler.handle(this.request, this.response);
|
||||
Publisher<Void> 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<Void> publisher = this.dispatcherHandler.handle(this.request, this.response);
|
||||
Publisher<Void> 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<Void> publisher = this.dispatcherHandler.handle(this.request, this.response);
|
||||
Publisher<Void> 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<Void> publisher = this.dispatcherHandler.handle(this.request, this.response);
|
||||
Publisher<Void> 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<Void> publisher = this.dispatcherHandler.handle(this.request, this.response);
|
||||
Publisher<Void> 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<Void> publisher = this.dispatcherHandler.handle(this.request, this.response);
|
||||
Publisher<Void> 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<Void> publisher = this.dispatcherHandler.handle(this.request, this.response);
|
||||
Publisher<Void> 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<Void> publisher = httpHandler.handle(this.request, this.response);
|
||||
WebExceptionHandler exceptionHandler = new ServerError500ExceptionHandler();
|
||||
WebHandler webHandler = new ExceptionHandlingWebHandler(this.dispatcherHandler, exceptionHandler);
|
||||
Publisher<Void> 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<Void> publisher = httpHandler.handle(this.request, this.response);
|
||||
WebHandler webHandler = new FilteringWebHandler(this.dispatcherHandler, new TestWebFilter());
|
||||
webHandler = new ExceptionHandlingWebHandler(webHandler, new ServerError500ExceptionHandler());
|
||||
Publisher<Void> 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<Void> handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) {
|
||||
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
public Mono<Void> 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<Void> filter(ServerHttpRequest req, ServerHttpResponse res, HttpFilterChain chain) {
|
||||
return chain.filter(req, res);
|
||||
public Mono<Void> filter(WebServerExchange exchange, WebFilterChain chain) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Void> publisher = this.handler.handle(this.request, this.response, ex);
|
||||
Publisher<Void> 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<Void> publisher = this.handler.handle(this.request, this.response, ex);
|
||||
Publisher<Void> publisher = this.handler.handle(this.exchange, ex);
|
||||
|
||||
List<Signal<Void>> signals = Streams.from(publisher).materialize().toList().get();
|
||||
List<Signal<Void>> signals = Stream.from(publisher).materialize().toList().get();
|
||||
assertEquals(1, signals.size());
|
||||
assertTrue(signals.get(0).hasError());
|
||||
assertSame(ex, signals.get(0).getThrowable());
|
||||
|
|
|
|||
|
|
@ -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<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return response.setBody(Stream.just(Buffer.wrap("foo").byteBuffer()));
|
||||
public Mono<Void> 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<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return response.setBody(Stream.just(Buffer.wrap("bar").byteBuffer()));
|
||||
public Mono<Void> 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<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
response.getHeaders().add("foo", "bar");
|
||||
public Mono<Void> handle(WebServerExchange exchange) {
|
||||
exchange.getResponse().getHeaders().add("foo", "bar");
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
List<HandlerResult> values = Streams.from(publisher).toList().get();
|
||||
Publisher<HandlerResult> publisher = hm.invokeForRequest(this.exchange);
|
||||
List<HandlerResult> 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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
List<HandlerResult> values = Streams.from(publisher).toList().get();
|
||||
Publisher<HandlerResult> publisher = hm.invokeForRequest(this.exchange);
|
||||
List<HandlerResult> 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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
List<HandlerResult> values = Streams.from(publisher).toList().get();
|
||||
Publisher<HandlerResult> publisher = hm.invokeForRequest(this.exchange);
|
||||
List<HandlerResult> 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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
List<HandlerResult> values = Streams.from(publisher).toList().get();
|
||||
Publisher<HandlerResult> publisher = hm.invokeForRequest(this.exchange);
|
||||
List<HandlerResult> 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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
Publisher<HandlerResult> 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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
Publisher<HandlerResult> 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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
Publisher<HandlerResult> 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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
Publisher<HandlerResult> 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<HandlerResult> publisher = hm.invokeForRequest(this.request);
|
||||
Publisher<HandlerResult> 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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Void> 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<Void> 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<Void> handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<Void> handle(WebServerExchange exchange) {
|
||||
if (this.raise) {
|
||||
throw this.exception;
|
||||
}
|
||||
return Mono.error(this.exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static class BadRequestExceptionHandler implements WebExceptionHandler {
|
||||
|
||||
@Override
|
||||
public Mono<Void> 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<Void> handle(WebServerExchange exchange, Throwable ex) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<Void> filter(ServerHttpRequest req, ServerHttpResponse res,
|
||||
HttpFilterChain chain) {
|
||||
|
||||
public Mono<Void> filter(WebServerExchange exchange, WebFilterChain chain) {
|
||||
this.invoked = true;
|
||||
return doFilter(req, res, chain);
|
||||
return doFilter(exchange, chain);
|
||||
}
|
||||
|
||||
public Mono<Void> doFilter(ServerHttpRequest req, ServerHttpResponse res,
|
||||
HttpFilterChain chain) {
|
||||
|
||||
return chain.filter(req, res);
|
||||
public Mono<Void> doFilter(WebServerExchange exchange, WebFilterChain chain) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ShortcircuitingFilter extends TestFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerHttpRequest req, ServerHttpResponse res,
|
||||
HttpFilterChain chain) {
|
||||
|
||||
public Mono<Void> doFilter(WebServerExchange exchange, WebFilterChain chain) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -142,10 +135,10 @@ public class FilterChainHttpHandlerTests {
|
|||
private static class AsyncFilter extends TestFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerHttpRequest req, ServerHttpResponse res, HttpFilterChain chain) {
|
||||
public Mono<Void> 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<Void> handle(ServerHttpRequest req, ServerHttpResponse res) {
|
||||
public Mono<Void> handle(WebServerExchange exchange) {
|
||||
logger.trace("StubHandler invoked.");
|
||||
this.invoked = true;
|
||||
return Mono.empty();
|
||||
Loading…
Reference in New Issue