Non-blocking HandlerMapping chain
This commit is contained in:
@@ -19,6 +19,7 @@ package org.springframework.web.reactive;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -30,7 +31,6 @@ 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.HttpStatus;
|
||||
import org.springframework.http.server.ReactiveHttpHandler;
|
||||
import org.springframework.http.server.ReactiveServerHttpRequest;
|
||||
import org.springframework.http.server.ReactiveServerHttpResponse;
|
||||
@@ -76,6 +76,7 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex
|
||||
context, HandlerMapping.class, true, false);
|
||||
|
||||
this.handlerMappings = new ArrayList<>(mappingBeans.values());
|
||||
this.handlerMappings.add(new NotFoundHandlerMapping());
|
||||
AnnotationAwareOrderComparator.sort(this.handlerMappings);
|
||||
|
||||
Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
@@ -98,13 +99,9 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex
|
||||
logger.debug("Processing " + request.getMethod() + " request for [" + request.getURI() + "]");
|
||||
}
|
||||
|
||||
Publisher<Object> handlerPublisher = getHandler(request);
|
||||
if (handlerPublisher == null) {
|
||||
// No exception handling mechanism yet
|
||||
response.setStatusCode(HttpStatus.NOT_FOUND);
|
||||
response.writeHeaders();
|
||||
return Publishers.empty();
|
||||
}
|
||||
Publisher<HandlerMapping> mappings = Publishers.from(this.handlerMappings);
|
||||
Publisher<Object> handlerPublisher = Publishers.concatMap(mappings, m -> m.getHandler(request));
|
||||
handlerPublisher = first(handlerPublisher);
|
||||
|
||||
Publisher<HandlerResult> resultPublisher = Publishers.concatMap(handlerPublisher, handler -> {
|
||||
HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
|
||||
@@ -117,16 +114,6 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex
|
||||
});
|
||||
}
|
||||
|
||||
protected Publisher<Object> getHandler(ReactiveServerHttpRequest request) {
|
||||
for (HandlerMapping handlerMapping : this.handlerMappings) {
|
||||
Publisher<Object> handlerPublisher = handlerMapping.getHandler(request);
|
||||
if (handlerPublisher != null) {
|
||||
return handlerPublisher;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected HandlerAdapter getHandlerAdapter(Object handler) {
|
||||
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
|
||||
if (handlerAdapter.supports(handler)) {
|
||||
@@ -145,4 +132,21 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex
|
||||
throw new IllegalStateException("No HandlerResultHandler: " + handlerResult.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static <E> Publisher<E> first(Publisher<E> source) {
|
||||
return Publishers.lift(source, (e, subscriber) -> {
|
||||
subscriber.onNext(e);
|
||||
subscriber.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
private static class NotFoundHandlerMapping implements HandlerMapping {
|
||||
|
||||
@Override
|
||||
public Publisher<Object> getHandler(ReactiveServerHttpRequest request) {
|
||||
return Publishers.error(new HandlerNotFoundException(request.getMethod(),
|
||||
request.getURI().getPath(), request.getHeaders()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.reactive;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HandlerNotFoundException extends NestedRuntimeException {
|
||||
|
||||
private final HttpMethod method;
|
||||
|
||||
private final String requestURL;
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for NoHandlerFoundException.
|
||||
* @param method the HTTP method
|
||||
* @param requestURL the HTTP request URL
|
||||
* @param headers the HTTP request headers
|
||||
*/
|
||||
public HandlerNotFoundException(HttpMethod method, String requestURL, HttpHeaders headers) {
|
||||
super("No handler found for " + method + " " + requestURL);
|
||||
this.method = method;
|
||||
this.requestURL = requestURL;
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
|
||||
public HttpMethod getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public String getRequestURL() {
|
||||
return this.requestURL;
|
||||
}
|
||||
|
||||
public HttpHeaders getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.util.Map;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.Publishers;
|
||||
import reactor.core.publisher.PublisherFactory;
|
||||
|
||||
import org.springframework.http.server.ReactiveServerHttpRequest;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
@@ -43,9 +44,14 @@ public class SimpleUrlHandlerMapping implements HandlerMapping {
|
||||
|
||||
@Override
|
||||
public Publisher<Object> getHandler(ReactiveServerHttpRequest request) {
|
||||
String path = request.getURI().getPath();
|
||||
Object handler = this.handlerMap.get(path);
|
||||
return (handler != null ? Publishers.just(handler) : null);
|
||||
return PublisherFactory.create(subscriber -> {
|
||||
String path = request.getURI().getPath();
|
||||
Object handler = this.handlerMap.get(path);
|
||||
if (handler != null) {
|
||||
subscriber.onNext(handler);
|
||||
}
|
||||
subscriber.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,19 +28,19 @@ import java.util.TreeSet;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.Publishers;
|
||||
import reactor.core.publisher.PublisherFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.http.server.ReactiveServerHttpRequest;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
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.method.HandlerMethodSelector;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
|
||||
|
||||
/**
|
||||
@@ -95,18 +95,21 @@ public class RequestMappingHandlerMapping implements HandlerMapping,
|
||||
|
||||
@Override
|
||||
public Publisher<Object> getHandler(ReactiveServerHttpRequest request) {
|
||||
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : this.methodMap.entrySet()) {
|
||||
RequestMappingInfo info = entry.getKey();
|
||||
if (info.matchesRequest(request)) {
|
||||
HandlerMethod handlerMethod = entry.getValue();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapped " + request.getMethod() + " " +
|
||||
request.getURI().getPath() + " to [" + handlerMethod + "]");
|
||||
return PublisherFactory.create(subscriber -> {
|
||||
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : this.methodMap.entrySet()) {
|
||||
RequestMappingInfo info = entry.getKey();
|
||||
if (info.matchesRequest(request)) {
|
||||
HandlerMethod handlerMethod = entry.getValue();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapped " + request.getMethod() + " " +
|
||||
request.getURI().getPath() + " to [" + handlerMethod + "]");
|
||||
}
|
||||
subscriber.onNext(handlerMethod);
|
||||
break;
|
||||
}
|
||||
return Publishers.just(handlerMethod);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
subscriber.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.io.buffer.Buffer;
|
||||
@@ -31,11 +32,14 @@ import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.server.ReactiveServerHttpRequest;
|
||||
import org.springframework.http.server.ReactiveServerHttpResponse;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
import org.springframework.http.server.AbstractHttpHandlerIntegrationTests;
|
||||
import org.springframework.http.server.ReactiveHttpHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
import org.springframework.web.reactive.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -89,6 +93,24 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
|
||||
assertArrayEquals("bar".getBytes(UTF_8), response.getBody());
|
||||
}
|
||||
|
||||
// TODO: remove @Ignore after 404 default handling
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testNotFound() throws Exception {
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
URI url = new URI("http://localhost:" + port + "/oops");
|
||||
RequestEntity<Void> request = RequestEntity.get(url).build();
|
||||
try {
|
||||
restTemplate.exchange(request, byte[].class);
|
||||
}
|
||||
catch (HttpClientErrorException ex) {
|
||||
assertEquals(HttpStatus.NOT_FOUND, ex.getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class TestHandlerMapping extends SimpleUrlHandlerMapping {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user