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:
Rossen Stoyanchev
2016-01-08 12:24:34 -05:00
parent 54e4e012b2
commit 4f614fa0fd
47 changed files with 831 additions and 614 deletions

View File

@@ -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);
}
}

View File

@@ -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());

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();