Updates to WebHandler support
Rename two classes each adapting to WebHandler to avoid confusing them: 1. HttpWebHandlerAdapter adapts from the low level HttpHandler to any WebHandler (e.g. DispatcherHandler). 2. SimpleHandlerAdapter adapts the plain WebHandler for use within the DispatcherHandler. This commit also fixes an issue in WebHttpHandlerBuilder to ensure that WebExceptionHandler's are inserted before and not after WebFilter's.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.web.reactive.result;
|
package org.springframework.web.reactive.result;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
@@ -27,15 +26,16 @@ import org.springframework.web.server.WebHandler;
|
|||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter to use a {@link WebHandler} through the {@link DispatcherHandler}.
|
* HandlerAdapter that allows using the plain {@link WebHandler} contract with
|
||||||
|
* the generic {@link DispatcherHandler}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
*/
|
*/
|
||||||
public class WebHandlerHandlerAdapter implements HandlerAdapter {
|
public class SimpleHandlerAdapter implements HandlerAdapter {
|
||||||
|
|
||||||
private static final ResolvableType PUBLISHER_VOID = ResolvableType.forClassWithGenerics(
|
private static final ResolvableType MONO_VOID = ResolvableType.forClassWithGenerics(
|
||||||
Publisher.class, Void.class);
|
Mono.class, Void.class);
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,7 +47,7 @@ public class WebHandlerHandlerAdapter implements HandlerAdapter {
|
|||||||
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
|
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
|
||||||
WebHandler webHandler = (WebHandler) handler;
|
WebHandler webHandler = (WebHandler) handler;
|
||||||
Mono<Void> mono = webHandler.handle(exchange);
|
Mono<Void> mono = webHandler.handle(exchange);
|
||||||
return Mono.just(new HandlerResult(webHandler, mono, PUBLISHER_VOID));
|
return Mono.just(new HandlerResult(webHandler, mono, MONO_VOID));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -18,19 +18,18 @@ package org.springframework.web.server;
|
|||||||
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.web.server.adapter.WebHttpHandlerAdapter;
|
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
|
||||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract to handle a web server exchange.
|
* Contract to handle a web request.
|
||||||
*
|
*
|
||||||
* <p>Use {@link WebHttpHandlerAdapter} to adapt a {@code WebHandler} to an
|
* <p>Use {@link HttpWebHandlerAdapter} to adapt a {@code WebHandler} to an
|
||||||
* {@link org.springframework.http.server.reactive.HttpHandler HttpHandler}.
|
* {@link org.springframework.http.server.reactive.HttpHandler HttpHandler}.
|
||||||
* The {@link WebHttpHandlerBuilder} provides a convenient way to do that while
|
* The {@link WebHttpHandlerBuilder} provides a convenient way to do that while
|
||||||
* also optionally configuring one or more filters and/or exception handlers.
|
* also optionally configuring one or more filters and/or exception handlers.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @see WebHttpHandlerBuilder
|
|
||||||
*/
|
*/
|
||||||
public interface WebHandler {
|
public interface WebHandler {
|
||||||
|
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ import org.springframework.web.server.session.WebSessionManager;
|
|||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class WebHttpHandlerAdapter extends WebHandlerDecorator implements HttpHandler {
|
public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler {
|
||||||
|
|
||||||
private static Log logger = LogFactory.getLog(WebHttpHandlerAdapter.class);
|
private static Log logger = LogFactory.getLog(HttpWebHandlerAdapter.class);
|
||||||
|
|
||||||
|
|
||||||
private WebSessionManager sessionManager = new DefaultWebSessionManager();
|
private WebSessionManager sessionManager = new DefaultWebSessionManager();
|
||||||
|
|
||||||
|
|
||||||
public WebHttpHandlerAdapter(WebHandler delegate) {
|
public HttpWebHandlerAdapter(WebHandler delegate) {
|
||||||
super(delegate);
|
super(delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -31,28 +31,20 @@ import org.springframework.web.server.handler.FilteringWebHandler;
|
|||||||
import org.springframework.web.server.session.WebSessionManager;
|
import org.springframework.web.server.session.WebSessionManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build an {@link org.springframework.http.server.reactive.HttpHandler HttpHandler}
|
* Builder for an {@link HttpHandler} that adapts to a target {@link WebHandler}
|
||||||
* to handle requests with a chain of {@link #filters(WebFilter...) web filters},
|
* along with a chain of {@link WebFilter}s and a set of
|
||||||
* a target {@link #webHandler(WebHandler) web handler}, and apply one or more
|
* {@link WebExceptionHandler}s.
|
||||||
* {@link #exceptionHandlers(WebExceptionHandler...) exception handlers}.
|
|
||||||
*
|
|
||||||
* <p>Effective this sets up the following {@code WebHandler} delegation:<br>
|
|
||||||
* {@link WebHttpHandlerAdapter} {@code -->}
|
|
||||||
* {@link ExceptionHandlingWebHandler} {@code -->}
|
|
||||||
* {@link FilteringWebHandler} {@code -->}
|
|
||||||
* {@link WebHandler}
|
|
||||||
*
|
*
|
||||||
* <p>Example usage:
|
* <p>Example usage:
|
||||||
* <pre>
|
* <pre>
|
||||||
* WebFilter myFilter = ... ;
|
* WebFilter filter = ... ;
|
||||||
* WebHandler myHandler = ... ;
|
* WebHandler webHandler = ... ;
|
||||||
|
* WebExceptionHandler exceptionHandler = ...;
|
||||||
*
|
*
|
||||||
* HttpHandler httpHandler = WebToHttpHandlerBuilder.webHandler(myHandler)
|
* HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(webHandler)
|
||||||
* .filters(myFilter)
|
* .filters(filter)
|
||||||
* .exceptionHandlers(new ResponseStatusExceptionHandler())
|
* .exceptionHandlers(exceptionHandler)
|
||||||
* .build();
|
* .build();
|
||||||
*
|
|
||||||
* // Configure the HttpServer with the created httpHandler
|
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
@@ -70,7 +62,7 @@ public class WebHttpHandlerBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor.
|
* Private constructor.
|
||||||
* See static factory method {@link #webHandler(WebHandler)}.
|
* See factory method {@link #webHandler(WebHandler)}.
|
||||||
*/
|
*/
|
||||||
private WebHttpHandlerBuilder(WebHandler targetHandler) {
|
private WebHttpHandlerBuilder(WebHandler targetHandler) {
|
||||||
Assert.notNull(targetHandler, "'targetHandler' must not be null");
|
Assert.notNull(targetHandler, "'targetHandler' must not be null");
|
||||||
@@ -80,10 +72,10 @@ public class WebHttpHandlerBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method to create a new builder instance.
|
* Factory method to create a new builder instance.
|
||||||
* @param targetHandler the target handler to process requests with
|
* @param webHandler the target handler for the request
|
||||||
*/
|
*/
|
||||||
public static WebHttpHandlerBuilder webHandler(WebHandler targetHandler) {
|
public static WebHttpHandlerBuilder webHandler(WebHandler webHandler) {
|
||||||
return new WebHttpHandlerBuilder(targetHandler);
|
return new WebHttpHandlerBuilder(webHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -114,6 +106,7 @@ public class WebHttpHandlerBuilder {
|
|||||||
* {@link ServerWebExchange WebServerExchange}
|
* {@link ServerWebExchange WebServerExchange}
|
||||||
* created for each HTTP request.
|
* created for each HTTP request.
|
||||||
* @param sessionManager the session manager
|
* @param sessionManager the session manager
|
||||||
|
* @see HttpWebHandlerAdapter#setSessionManager(WebSessionManager)
|
||||||
*/
|
*/
|
||||||
public WebHttpHandlerBuilder sessionManager(WebSessionManager sessionManager) {
|
public WebHttpHandlerBuilder sessionManager(WebSessionManager sessionManager) {
|
||||||
this.sessionManager = sessionManager;
|
this.sessionManager = sessionManager;
|
||||||
@@ -124,35 +117,20 @@ public class WebHttpHandlerBuilder {
|
|||||||
* Build the {@link HttpHandler}.
|
* Build the {@link HttpHandler}.
|
||||||
*/
|
*/
|
||||||
public HttpHandler build() {
|
public HttpHandler build() {
|
||||||
WebHandler handler = createWebHandler();
|
|
||||||
return adaptWebHandler(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the final (decorated) {@link WebHandler} to use.
|
|
||||||
*/
|
|
||||||
protected WebHandler createWebHandler() {
|
|
||||||
WebHandler webHandler = this.targetHandler;
|
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()) {
|
if (!this.filters.isEmpty()) {
|
||||||
WebFilter[] array = new WebFilter[this.filters.size()];
|
WebFilter[] array = new WebFilter[this.filters.size()];
|
||||||
webHandler = new FilteringWebHandler(webHandler, this.filters.toArray(array));
|
webHandler = new FilteringWebHandler(webHandler, this.filters.toArray(array));
|
||||||
}
|
}
|
||||||
return webHandler;
|
if (!this.exceptionHandlers.isEmpty()) {
|
||||||
}
|
WebExceptionHandler[] array = new WebExceptionHandler[this.exceptionHandlers.size()];
|
||||||
|
webHandler = new ExceptionHandlingWebHandler(webHandler, this.exceptionHandlers.toArray(array));
|
||||||
/**
|
|
||||||
* Adapt the {@link WebHandler} to {@link HttpHandler}.
|
|
||||||
*/
|
|
||||||
protected WebHttpHandlerAdapter adaptWebHandler(WebHandler handler) {
|
|
||||||
WebHttpHandlerAdapter adapter = new WebHttpHandlerAdapter(handler);
|
|
||||||
if (this.sessionManager != null) {
|
|
||||||
adapter.setSessionManager(this.sessionManager);
|
|
||||||
}
|
}
|
||||||
return adapter;
|
HttpWebHandlerAdapter httpHandler = new HttpWebHandlerAdapter(webHandler);
|
||||||
|
if (this.sessionManager != null) {
|
||||||
|
httpHandler.setSessionManager(this.sessionManager);
|
||||||
|
}
|
||||||
|
return httpHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -27,8 +27,8 @@ import org.springframework.web.server.WebHandler;
|
|||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebHandler that delegates to a chain of {@link WebFilter} instances followed
|
* WebHandler that delegates to a chain of {@link WebFilter} instances and then
|
||||||
* by a target {@link WebHandler}.
|
* to the target {@link WebHandler}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import static org.junit.Assert.assertEquals;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests with simple WebHandler's processing requests.
|
* Integration tests with requests mapped to plain {@link WebHandler}s.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
@@ -61,9 +61,9 @@ public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTe
|
|||||||
protected HttpHandler createHttpHandler() {
|
protected HttpHandler createHttpHandler() {
|
||||||
|
|
||||||
StaticApplicationContext wac = new StaticApplicationContext();
|
StaticApplicationContext wac = new StaticApplicationContext();
|
||||||
wac.registerSingleton("hm", TestHandlerMapping.class);
|
wac.registerSingleton("handlerMapping", TestSimpleUrlHandlerMapping.class);
|
||||||
wac.registerSingleton("ha", WebHandlerHandlerAdapter.class);
|
wac.registerSingleton("handlerAdapter", SimpleHandlerAdapter.class);
|
||||||
wac.registerSingleton("rh", SimpleResultHandler.class);
|
wac.registerSingleton("resultHandler", SimpleResultHandler.class);
|
||||||
wac.refresh();
|
wac.refresh();
|
||||||
|
|
||||||
DispatcherHandler dispatcherHandler = new DispatcherHandler();
|
DispatcherHandler dispatcherHandler = new DispatcherHandler();
|
||||||
@@ -129,9 +129,9 @@ public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTe
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class TestHandlerMapping extends SimpleUrlHandlerMapping {
|
private static class TestSimpleUrlHandlerMapping extends SimpleUrlHandlerMapping {
|
||||||
|
|
||||||
public TestHandlerMapping() {
|
public TestSimpleUrlHandlerMapping() {
|
||||||
Map<String, Object> map = new HashMap<>();
|
Map<String, Object> map = new HashMap<>();
|
||||||
map.put("/foo", new FooHandler());
|
map.put("/foo", new FooHandler());
|
||||||
map.put("/bar", new BarHandler());
|
map.put("/bar", new BarHandler());
|
||||||
@@ -140,7 +140,6 @@ public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static DataBuffer asDataBuffer(String text) {
|
private static DataBuffer asDataBuffer(String text) {
|
||||||
return new DefaultDataBufferAllocator().allocateBuffer().write(text.getBytes(StandardCharsets.UTF_8));
|
return new DefaultDataBufferAllocator().allocateBuffer().write(text.getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,20 +23,25 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.test.TestSubscriber;
|
||||||
|
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.server.reactive.HttpHandler;
|
import org.springframework.http.server.reactive.HttpHandler;
|
||||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
|
import org.springframework.web.server.WebExceptionHandler;
|
||||||
import org.springframework.web.server.WebFilter;
|
import org.springframework.web.server.WebFilter;
|
||||||
import org.springframework.web.server.WebFilterChain;
|
import org.springframework.web.server.WebFilterChain;
|
||||||
import org.springframework.web.server.WebHandler;
|
import org.springframework.web.server.WebHandler;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,9 +52,9 @@ public class FilteringWebHandlerTests {
|
|||||||
private static Log logger = LogFactory.getLog(FilteringWebHandlerTests.class);
|
private static Log logger = LogFactory.getLog(FilteringWebHandlerTests.class);
|
||||||
|
|
||||||
|
|
||||||
private ServerHttpRequest request;
|
private MockServerHttpRequest request;
|
||||||
|
|
||||||
private ServerHttpResponse response;
|
private MockServerHttpResponse response;
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -108,6 +113,20 @@ public class FilteringWebHandlerTests {
|
|||||||
assertTrue(webHandler.invoked());
|
assertTrue(webHandler.invoked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleErrorFromFilter() throws Exception {
|
||||||
|
TestExceptionHandler exceptionHandler = new TestExceptionHandler();
|
||||||
|
HttpHandler handler = WebHttpHandlerBuilder.webHandler(new StubWebHandler())
|
||||||
|
.filters(new ExceptionFilter()).exceptionHandlers(exceptionHandler).build();
|
||||||
|
handler.handle(this.request, this.response).get();
|
||||||
|
|
||||||
|
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus());
|
||||||
|
|
||||||
|
Throwable savedException = exceptionHandler.ex;
|
||||||
|
assertNotNull(savedException);
|
||||||
|
assertEquals("boo", savedException.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
private HttpHandler createHttpHandler(StubWebHandler webHandler, WebFilter... filters) {
|
private HttpHandler createHttpHandler(StubWebHandler webHandler, WebFilter... filters) {
|
||||||
return WebHttpHandlerBuilder.webHandler(webHandler).filters(filters).build();
|
return WebHttpHandlerBuilder.webHandler(webHandler).filters(filters).build();
|
||||||
}
|
}
|
||||||
@@ -117,7 +136,6 @@ public class FilteringWebHandlerTests {
|
|||||||
|
|
||||||
private volatile boolean invoked;
|
private volatile boolean invoked;
|
||||||
|
|
||||||
|
|
||||||
public boolean invoked() {
|
public boolean invoked() {
|
||||||
return this.invoked;
|
return this.invoked;
|
||||||
}
|
}
|
||||||
@@ -156,6 +174,24 @@ public class FilteringWebHandlerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ExceptionFilter implements WebFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
return Mono.error(new IllegalStateException("boo"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestExceptionHandler implements WebExceptionHandler {
|
||||||
|
|
||||||
|
private Throwable ex;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
|
||||||
|
this.ex = ex;
|
||||||
|
return Mono.error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class StubWebHandler implements WebHandler {
|
private static class StubWebHandler implements WebHandler {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user