Refactor Router to RoutingFunctions
This commit refactors the Router into a RoutingFunctions class, by: - Renaming the class :) - Moving all Configuration logic into a separate, top-level Configuration class with mutable builder.
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.function;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
|
||||
/**
|
||||
* Defines the configuration to be used for processing {@link HandlerFunction}s. An instance of
|
||||
* this class is immutable, but a
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface Configuration {
|
||||
|
||||
// Static methods
|
||||
|
||||
/**
|
||||
* Return a mutable, empty builder for a {@code Configuration}.
|
||||
* @return the builder
|
||||
*/
|
||||
static Builder builder() {
|
||||
return new DefaultConfigurationBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mutable builder for a {@code Configuration} with a default initialization.
|
||||
* @return the builder
|
||||
*/
|
||||
static Builder defaultBuilder() {
|
||||
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
|
||||
builder.defaultConfiguration();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mutable builder based on the given {@linkplain ApplicationContext application context}.
|
||||
* The returned builder will search for all {@link HttpMessageReader}, {@link HttpMessageWriter},
|
||||
* and {@link ViewResolver} instances in the given application context and return them for
|
||||
* {@link #messageReaders()}, {@link #messageWriters()}, and {@link #viewResolvers()} in the
|
||||
* built configuration respectively.
|
||||
* @param applicationContext the application context to base the configuration on
|
||||
* @return the builder
|
||||
*/
|
||||
static Builder applicationContext(ApplicationContext applicationContext) {
|
||||
Assert.notNull(applicationContext, "'applicationContext' must not be null");
|
||||
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
|
||||
builder.applicationContext(applicationContext);
|
||||
return builder;
|
||||
}
|
||||
|
||||
// Instance methods
|
||||
|
||||
/**
|
||||
* Supply a {@linkplain Stream stream} of {@link HttpMessageReader}s to be used for request
|
||||
* body conversion.
|
||||
* @return the stream of message readers
|
||||
*/
|
||||
Supplier<Stream<HttpMessageReader<?>>> messageReaders();
|
||||
|
||||
/**
|
||||
* Supply a {@linkplain Stream stream} of {@link HttpMessageWriter}s to be used for response
|
||||
* body conversion.
|
||||
* @return the stream of message writers
|
||||
*/
|
||||
Supplier<Stream<HttpMessageWriter<?>>> messageWriters();
|
||||
|
||||
/**
|
||||
* Supply a {@linkplain Stream stream} of {@link ViewResolver}s to be used for view name
|
||||
* resolution.
|
||||
* @return the stream of view resolvers
|
||||
*/
|
||||
Supplier<Stream<ViewResolver>> viewResolvers();
|
||||
|
||||
|
||||
/**
|
||||
* A mutable builder for a {@link Configuration}.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Add the given message reader to this builder.
|
||||
* @param messageReader the message reader to add
|
||||
* @return this builder
|
||||
*/
|
||||
Builder messageReader(HttpMessageReader<?> messageReader);
|
||||
|
||||
/**
|
||||
* Add the given message writer to this builder.
|
||||
* @param messageWriter the message writer to add
|
||||
* @return this builder
|
||||
*/
|
||||
Builder messageWriter(HttpMessageWriter<?> messageWriter);
|
||||
|
||||
/**
|
||||
* Add the given view resolver to this builder.
|
||||
* @param viewResolver the view resolver to add
|
||||
* @return this builder
|
||||
*/
|
||||
Builder viewResolver(ViewResolver viewResolver);
|
||||
|
||||
/**
|
||||
* Builds the {@link Configuration}.
|
||||
* @return the built configuration
|
||||
*/
|
||||
Configuration build();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.function;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.http.codec.DecoderHttpMessageReader;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
|
||||
/**
|
||||
* A default implementation of configuration.
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
class DefaultConfiguration implements Router.Configuration {
|
||||
|
||||
private static final boolean jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
||||
DefaultConfiguration.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
|
||||
DefaultConfiguration.class.getClassLoader());
|
||||
|
||||
private static final boolean jaxb2Present =
|
||||
ClassUtils.isPresent("javax.xml.bind.Binder", DefaultConfiguration.class.getClassLoader());
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders = new ArrayList<>();
|
||||
|
||||
private final List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
||||
|
||||
private final List<ViewResolver> viewResolvers = new ArrayList<>();
|
||||
|
||||
public DefaultConfiguration() {
|
||||
this.messageReaders.add(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
||||
this.messageReaders.add(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
this.messageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||
this.messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
||||
if (jaxb2Present) {
|
||||
this.messageReaders.add(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
|
||||
this.messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
this.messageReaders.add(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
|
||||
this.messageWriters.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
|
||||
return this.messageReaders::stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() {
|
||||
return this.messageWriters::stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<ViewResolver>> viewResolvers() {
|
||||
return this.viewResolvers::stream;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.function;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.http.codec.DecoderHttpMessageReader;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
class DefaultConfigurationBuilder implements Configuration.Builder {
|
||||
|
||||
private static final boolean jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
||||
DefaultConfigurationBuilder.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
|
||||
DefaultConfigurationBuilder.class.getClassLoader());
|
||||
|
||||
private static final boolean jaxb2Present =
|
||||
ClassUtils.isPresent("javax.xml.bind.Binder",
|
||||
DefaultConfigurationBuilder.class.getClassLoader());
|
||||
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders = new ArrayList<>();
|
||||
|
||||
private final List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
||||
|
||||
private final List<ViewResolver> viewResolvers = new ArrayList<>();
|
||||
|
||||
public void defaultConfiguration() {
|
||||
messageReader(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
||||
messageReader(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
messageWriter(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||
messageWriter(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
|
||||
if (jaxb2Present) {
|
||||
messageReader(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
|
||||
messageWriter(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
messageReader(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
|
||||
messageWriter(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
|
||||
}
|
||||
}
|
||||
|
||||
public void applicationContext(ApplicationContext applicationContext) {
|
||||
applicationContext.getBeansOfType(HttpMessageReader.class).values().forEach(this::messageReader);
|
||||
applicationContext.getBeansOfType(HttpMessageWriter.class).values().forEach(this::messageWriter);
|
||||
applicationContext.getBeansOfType(ViewResolver.class).values().forEach(this::viewResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration.Builder messageReader(HttpMessageReader<?> messageReader) {
|
||||
Assert.notNull(messageReader, "'messageReader' must not be null");
|
||||
this.messageReaders.add(messageReader);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration.Builder messageWriter(HttpMessageWriter<?> messageWriter) {
|
||||
Assert.notNull(messageWriter, "'messageWriter' must not be null");
|
||||
this.messageWriters.add(messageWriter);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration.Builder viewResolver(ViewResolver viewResolver) {
|
||||
Assert.notNull(viewResolver, "'viewResolver' must not be null");
|
||||
this.viewResolvers.add(viewResolver);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration build() {
|
||||
return new DefaultConfiguration(this.messageReaders, this.messageWriters,
|
||||
this.viewResolvers);
|
||||
}
|
||||
|
||||
private static class DefaultConfiguration implements Configuration {
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
private final List<HttpMessageWriter<?>> messageWriters;
|
||||
|
||||
private final List<ViewResolver> viewResolvers;
|
||||
|
||||
public DefaultConfiguration(
|
||||
List<HttpMessageReader<?>> messageReaders,
|
||||
List<HttpMessageWriter<?>> messageWriters,
|
||||
List<ViewResolver> viewResolvers) {
|
||||
this.messageReaders = unmodifiableCopy(messageReaders);
|
||||
this.messageWriters = unmodifiableCopy(messageWriters);
|
||||
this.viewResolvers = unmodifiableCopy(viewResolvers);
|
||||
}
|
||||
|
||||
private static <T> List<T> unmodifiableCopy(List<? extends T> list) {
|
||||
return Collections.unmodifiableList(new ArrayList<>(list));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
|
||||
return this.messageReaders::stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() {
|
||||
return this.messageWriters::stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<ViewResolver>> viewResolvers() {
|
||||
return this.viewResolvers::stream;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,7 +95,7 @@ class DefaultRequest implements Request {
|
||||
|
||||
@Override
|
||||
public Map<String, String> pathVariables() {
|
||||
return this.exchange.<Map<String, String>>getAttribute(Router.URI_TEMPLATE_VARIABLES_ATTRIBUTE).
|
||||
return this.exchange.<Map<String, String>>getAttribute(RoutingFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE).
|
||||
orElseGet(Collections::emptyMap);
|
||||
}
|
||||
|
||||
@@ -186,13 +186,14 @@ class DefaultRequest implements Request {
|
||||
Function<HttpMessageReader<T>, S> readerFunction) {
|
||||
ResolvableType elementType = ResolvableType.forClass(targetClass);
|
||||
MediaType contentType = headers.contentType().orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
return messageReaderStream(exchange)
|
||||
Supplier<Stream<HttpMessageReader<?>>> messageReaderStream = configuration(exchange).messageReaders();
|
||||
return messageReaderStream.get()
|
||||
.filter(r -> r.canRead(elementType, contentType, Collections.emptyMap()))
|
||||
.findFirst()
|
||||
.map(CastingUtils::<T>cast)
|
||||
.map(readerFunction)
|
||||
.orElseGet(() -> {
|
||||
List<MediaType> supportedMediaTypes = messageReaderStream(exchange)
|
||||
List<MediaType> supportedMediaTypes = messageReaderStream.get()
|
||||
.flatMap(messageReader -> messageReader.getReadableMediaTypes().stream())
|
||||
.collect(Collectors.toList());
|
||||
return cast(
|
||||
@@ -200,10 +201,11 @@ class DefaultRequest implements Request {
|
||||
});
|
||||
}
|
||||
|
||||
private Stream<HttpMessageReader<?>> messageReaderStream(ServerWebExchange exchange) {
|
||||
return exchange.<Supplier<Stream<HttpMessageReader<?>>>>getAttribute(Router.HTTP_MESSAGE_READERS_ATTRIBUTE)
|
||||
.orElseThrow(() -> new IllegalStateException("Could not find HttpMessageReaders in ServerWebExchange"))
|
||||
.get();
|
||||
private Configuration configuration(ServerWebExchange exchange) {
|
||||
return exchange.<Configuration>getAttribute(
|
||||
RoutingFunctions.CONFIGURATION_ATTRIBUTE)
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"Could not find Configuration in ServerWebExchange"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -55,9 +55,9 @@ abstract class DefaultResponses {
|
||||
|
||||
private static final boolean jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
||||
DefaultConfiguration.class.getClassLoader()) &&
|
||||
DefaultResponses.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
|
||||
DefaultConfiguration.class.getClassLoader());
|
||||
DefaultResponses.class.getClassLoader());
|
||||
|
||||
|
||||
public static Response<Void> empty(int statusCode, HttpHeaders headers) {
|
||||
@@ -134,7 +134,8 @@ abstract class DefaultResponses {
|
||||
exchange -> {
|
||||
MediaType contentType = exchange.getResponse().getHeaders().getContentType();
|
||||
Locale locale = Locale.ENGLISH; // TODO: resolve locale
|
||||
return Flux.fromStream(viewResolverStream(exchange))
|
||||
Stream<ViewResolver> viewResolverStream = configuration(exchange).viewResolvers().get();
|
||||
return Flux.fromStream(viewResolverStream)
|
||||
.concatMap(viewResolver -> viewResolver.resolveViewName(name, locale))
|
||||
.next()
|
||||
.otherwiseIfEmpty(Mono.error(new IllegalArgumentException("Could not resolve view with name '" + name +"'")))
|
||||
@@ -153,9 +154,12 @@ abstract class DefaultResponses {
|
||||
private static <T> Mono<Void> writeWithMessageWriters(ServerWebExchange exchange,
|
||||
Publisher<T> body,
|
||||
ResolvableType bodyType) {
|
||||
|
||||
// TODO: use ContentNegotiatingResultHandlerSupport
|
||||
MediaType contentType = exchange.getResponse().getHeaders().getContentType();
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
return messageWriterStream(exchange)
|
||||
Stream<HttpMessageWriter<?>> messageWriterStream = configuration(exchange).messageWriters().get();
|
||||
return messageWriterStream
|
||||
.filter(messageWriter -> messageWriter.canWrite(bodyType, contentType, Collections
|
||||
.emptyMap()))
|
||||
.findFirst()
|
||||
@@ -168,20 +172,11 @@ abstract class DefaultResponses {
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<HttpMessageWriter<?>> messageWriterStream(ServerWebExchange exchange) {
|
||||
return exchange.<Supplier<Stream<HttpMessageWriter<?>>>>getAttribute(
|
||||
Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE)
|
||||
private static Configuration configuration(ServerWebExchange exchange) {
|
||||
return exchange.<Configuration>getAttribute(
|
||||
RoutingFunctions.CONFIGURATION_ATTRIBUTE)
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"Could not find HttpMessageWriters in ServerWebExchange"))
|
||||
.get();
|
||||
}
|
||||
|
||||
private static Stream<ViewResolver> viewResolverStream(ServerWebExchange exchange) {
|
||||
return exchange.<Supplier<Stream<ViewResolver>>>getAttribute(
|
||||
Router.VIEW_RESOLVERS_ATTRIBUTE)
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"Could not find ViewResolvers in ServerWebExchange"))
|
||||
.get();
|
||||
"Could not find Configuration in ServerWebExchange"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.client.reactive.BodyExtractor;
|
||||
|
||||
/**
|
||||
* Represents an HTTP request, as handled by a {@linkplain HandlerFunction handler function}.
|
||||
@@ -68,6 +69,7 @@ public interface Request {
|
||||
* Return the body of this request.
|
||||
*/
|
||||
Body body();
|
||||
// <T> T body(BodyExtractor<T> extractor);
|
||||
|
||||
/**
|
||||
* Return the request attribute value if present.
|
||||
|
||||
@@ -25,8 +25,8 @@ import org.springframework.util.Assert;
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
* @see RequestPredicates
|
||||
* @see Router#route(RequestPredicate, HandlerFunction)
|
||||
* @see Router#subroute(RequestPredicate, RoutingFunction)
|
||||
* @see RoutingFunctions#route(RequestPredicate, HandlerFunction)
|
||||
* @see RoutingFunctions#subroute(RequestPredicate, RoutingFunction)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RequestPredicate {
|
||||
|
||||
@@ -249,7 +249,7 @@ public abstract class RequestPredicates {
|
||||
if (request instanceof DefaultRequest) {
|
||||
DefaultRequest defaultRequest = (DefaultRequest) request;
|
||||
Map<String, String> uriTemplateVariables = this.pathMatcher.extractUriTemplateVariables(this.pattern, path);
|
||||
defaultRequest.exchange().getAttributes().put(Router.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
||||
defaultRequest.exchange().getAttributes().put(RoutingFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -306,6 +306,8 @@ public interface Response<T> {
|
||||
*/
|
||||
BodyBuilder contentType(MediaType contentType);
|
||||
|
||||
// <T> Response<T> body(BodyPopulator<T> populator);
|
||||
|
||||
/**
|
||||
* Set the body of the response to the given object and return it.
|
||||
*
|
||||
@@ -322,6 +324,7 @@ public interface Response<T> {
|
||||
* @return the built response
|
||||
*/
|
||||
<T, S extends Publisher<T>> Response<S> stream(S publisher, Class<T> elementClass);
|
||||
// ResolvableType
|
||||
|
||||
/**
|
||||
* Set the body of the response to the given {@link Resource} and return it.
|
||||
@@ -350,6 +353,7 @@ public interface Response<T> {
|
||||
* @return the built response
|
||||
* @see <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events W3C recommendation</a>
|
||||
*/
|
||||
// remove?
|
||||
<T, S extends Publisher<T>> Response<S> sse(S eventsPublisher, Class<T> eventClass);
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,18 +18,12 @@ package org.springframework.web.reactive.function;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
|
||||
|
||||
@@ -54,42 +48,27 @@ import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class Router {
|
||||
public abstract class RoutingFunctions {
|
||||
|
||||
private static final HandlerFunction<Void> NOT_FOUND_HANDLER = request -> Response.notFound().build();
|
||||
|
||||
/**
|
||||
* Name of the {@link ServerWebExchange} attribute that contains the {@link Request}.
|
||||
*/
|
||||
public static final String REQUEST_ATTRIBUTE = Router.class.getName() + ".request";
|
||||
public static final String REQUEST_ATTRIBUTE = RoutingFunctions.class.getName() + ".request";
|
||||
|
||||
/**
|
||||
* Name of the {@link ServerWebExchange} attribute that contains a {@link Supplier} to the
|
||||
* {@linkplain Stream stream} of {@link HttpMessageReader}s obtained
|
||||
* from the {@linkplain Configuration#messageReaders() configuration}.
|
||||
* Name of the {@link ServerWebExchange} attribute that contains the
|
||||
* {@linkplain Configuration configuration} used throughout the routing and handling of the
|
||||
* request.
|
||||
*/
|
||||
public static final String HTTP_MESSAGE_READERS_ATTRIBUTE = Router.class.getName() + ".httpMessageReaders";
|
||||
|
||||
/**
|
||||
* Name of the {@link ServerWebExchange} attribute that contains a {@link Supplier} to the
|
||||
* {@linkplain Stream stream} of {@link HttpMessageWriter}s obtained
|
||||
* from the {@linkplain Configuration#messageWriters() configuration}.
|
||||
*/
|
||||
public static final String HTTP_MESSAGE_WRITERS_ATTRIBUTE = Router.class.getName() + ".httpMessageWriters";
|
||||
public static final String CONFIGURATION_ATTRIBUTE = RoutingFunctions.class.getName() + ".configuration";
|
||||
|
||||
/**
|
||||
* Name of the {@link ServerWebExchange} attribute that contains the URI
|
||||
* templates map, mapping variable names to values.
|
||||
*/
|
||||
public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE = Router.class.getName() + ".uriTemplateVariables";
|
||||
|
||||
/**
|
||||
* Name of the {@link ServerWebExchange} attribute that contains the {@link Supplier} to the
|
||||
* {@linkplain Stream stream} of {@link ViewResolver}s obtained
|
||||
* from the {@linkplain Configuration#viewResolvers() configuration}.
|
||||
*/
|
||||
public static final String VIEW_RESOLVERS_ATTRIBUTE = Router.class.getName() + ".viewResolvers";
|
||||
|
||||
public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE = RoutingFunctions.class.getName() + ".uriTemplateVariables";
|
||||
|
||||
/**
|
||||
* Route to the given handler function if the given request predicate applies.
|
||||
@@ -133,7 +112,7 @@ public abstract class Router {
|
||||
|
||||
/**
|
||||
* Converts the given {@linkplain RoutingFunction routing function} into a {@link HttpHandler}.
|
||||
* This conversion uses the {@linkplain #defaultConfiguration() default configuration}.
|
||||
* This conversion uses the {@linkplain Configuration#defaultBuilder() default configuration}.
|
||||
*
|
||||
* <p>The returned {@code HttpHandler} can be adapted to run in
|
||||
* {@linkplain org.springframework.http.server.reactive.ServletHttpHandlerAdapter Servlet 3.1+},
|
||||
@@ -178,7 +157,7 @@ public abstract class Router {
|
||||
|
||||
/**
|
||||
* Converts the given {@linkplain RoutingFunction routing function} into a {@link HandlerMapping}.
|
||||
* This conversion uses the {@linkplain #defaultConfiguration() default configuration}.
|
||||
* This conversion uses the {@linkplain Configuration#defaultBuilder() default configuration}.
|
||||
*
|
||||
* <p>The returned {@code HttpHandler} can be run in a
|
||||
* {@link org.springframework.web.reactive.DispatcherHandler}.
|
||||
@@ -217,13 +196,15 @@ public abstract class Router {
|
||||
};
|
||||
}
|
||||
|
||||
private static Configuration defaultConfiguration() {
|
||||
return Configuration.defaultBuilder().build();
|
||||
}
|
||||
|
||||
private static void addAttributes(ServerWebExchange exchange, Request request,
|
||||
Configuration configuration) {
|
||||
Map<String, Object> attributes = exchange.getAttributes();
|
||||
attributes.put(REQUEST_ATTRIBUTE, request);
|
||||
attributes.put(HTTP_MESSAGE_READERS_ATTRIBUTE, configuration.messageReaders());
|
||||
attributes.put(HTTP_MESSAGE_WRITERS_ATTRIBUTE, configuration.messageWriters());
|
||||
attributes.put(VIEW_RESOLVERS_ATTRIBUTE, configuration.viewResolvers());
|
||||
attributes.put(CONFIGURATION_ATTRIBUTE, configuration);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -231,69 +212,4 @@ public abstract class Router {
|
||||
return (HandlerFunction<T>) NOT_FOUND_HANDLER;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the default configuration.
|
||||
*/
|
||||
public static Configuration defaultConfiguration() {
|
||||
return new DefaultConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a configuration based on the given {@linkplain ApplicationContext application context}.
|
||||
* This configuration will search for all {@link HttpMessageReader} and {@link HttpMessageWriter}
|
||||
* instances in the given application context.
|
||||
* @param applicationContext the application context to base the configuration on
|
||||
* @return the configuration
|
||||
*/
|
||||
public static Configuration toConfiguration(ApplicationContext applicationContext) {
|
||||
return new Configuration() {
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
|
||||
return () -> applicationContext.getBeansOfType(HttpMessageReader.class).values().stream()
|
||||
.map(CastingUtils::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() {
|
||||
return () -> applicationContext.getBeansOfType(HttpMessageWriter.class).values().stream()
|
||||
.map(CastingUtils::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<ViewResolver>> viewResolvers() {
|
||||
return () -> applicationContext.getBeansOfType(ViewResolver.class).values().stream();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines the configuration to be used by this {@code Router}.
|
||||
*/
|
||||
public interface Configuration {
|
||||
|
||||
/**
|
||||
* Supply a {@linkplain Stream stream} of {@link HttpMessageReader}s to be used for request
|
||||
* body conversion.
|
||||
* @return the stream of message readers
|
||||
*/
|
||||
Supplier<Stream<HttpMessageReader<?>>> messageReaders();
|
||||
|
||||
/**
|
||||
* Supply a {@linkplain Stream stream} of {@link HttpMessageWriter}s to be used for response
|
||||
* body conversion.
|
||||
* @return the stream of message writers
|
||||
*/
|
||||
Supplier<Stream<HttpMessageWriter<?>>> messageWriters();
|
||||
|
||||
/**
|
||||
* Supply a {@linkplain Stream stream} of {@link ViewResolver}s to be used for view name
|
||||
* resolution.
|
||||
* @return the stream of view resolvers
|
||||
*/
|
||||
Supplier<Stream<ViewResolver>> viewResolvers();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.function.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.Request;
|
||||
import org.springframework.web.reactive.function.Response;
|
||||
import org.springframework.web.reactive.function.Router;
|
||||
import org.springframework.web.reactive.function.RoutingFunctions;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -58,7 +58,7 @@ public class HandlerFunctionAdapter implements HandlerAdapter {
|
||||
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
|
||||
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
|
||||
Request request =
|
||||
exchange.<Request>getAttribute(Router.REQUEST_ATTRIBUTE)
|
||||
exchange.<Request>getAttribute(RoutingFunctions.REQUEST_ATTRIBUTE)
|
||||
.orElseThrow(() -> new IllegalStateException("Could not find Request in exchange attributes"));
|
||||
|
||||
Response<?> response = handlerFunction.handle(request);
|
||||
|
||||
@@ -28,7 +28,7 @@ public abstract class AbstractRoutingFunctionIntegrationTests
|
||||
@Override
|
||||
protected final HttpHandler createHttpHandler() {
|
||||
RoutingFunction<?> routingFunction = routingFunction();
|
||||
return Router.toHttpHandler(routingFunction);
|
||||
return RoutingFunctions.toHttpHandler(routingFunction);
|
||||
}
|
||||
|
||||
protected abstract RoutingFunction<?> routingFunction();
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void toConfiguration() throws Exception {
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.registerSingleton("messageWriter", DummyMessageWriter.class);
|
||||
applicationContext.registerSingleton("messageReader", DummyMessageReader.class);
|
||||
applicationContext.refresh();
|
||||
|
||||
Configuration configuration = Configuration.toConfiguration(applicationContext);
|
||||
assertTrue(configuration.messageReaders().get()
|
||||
.allMatch(r -> r instanceof DummyMessageReader));
|
||||
assertTrue(configuration.messageWriters().get()
|
||||
.allMatch(r -> r instanceof DummyMessageWriter));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class DummyMessageWriter implements HttpMessageWriter<Object> {
|
||||
|
||||
@Override
|
||||
public boolean canWrite(ResolvableType type, MediaType mediaType, Map<String, Object> hints) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getWritableMediaTypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> write(Publisher<?> inputStream, ResolvableType type,
|
||||
MediaType contentType,
|
||||
ReactiveHttpOutputMessage outputMessage,
|
||||
Map<String, Object> hints) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyMessageReader implements HttpMessageReader<Object> {
|
||||
|
||||
@Override
|
||||
public boolean canRead(ResolvableType type, MediaType mediaType, Map<String, Object> hints) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getReadableMediaTypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Object> read(ResolvableType type, ReactiveHttpInputMessage inputMessage,
|
||||
Map<String, Object> hints) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage,
|
||||
Map<String, Object> hints) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class DefaultConfigurationTests {
|
||||
|
||||
private DefaultConfiguration configuration = new DefaultConfiguration();
|
||||
|
||||
@Test
|
||||
public void messageReaders() throws Exception {
|
||||
assertEquals(4, configuration.messageReaders().get().count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messageWriters() throws Exception {
|
||||
assertEquals(4, configuration.messageWriters().get().count());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -115,7 +115,7 @@ public class DefaultRequestTests {
|
||||
@Test
|
||||
public void pathVariables() throws Exception {
|
||||
Map<String, String> pathVariables = Collections.singletonMap("foo", "bar");
|
||||
when(mockExchange.getAttribute(Router.URI_TEMPLATE_VARIABLES_ATTRIBUTE)).thenReturn(Optional.of(pathVariables));
|
||||
when(mockExchange.getAttribute(RoutingFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE)).thenReturn(Optional.of(pathVariables));
|
||||
|
||||
assertEquals(pathVariables, defaultRequest.pathVariables());
|
||||
}
|
||||
@@ -161,9 +161,10 @@ public class DefaultRequestTests {
|
||||
|
||||
Set<HttpMessageReader<?>> messageReaders = Collections
|
||||
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
|
||||
when(mockExchange.getAttribute(Router.HTTP_MESSAGE_READERS_ATTRIBUTE))
|
||||
.thenReturn(Optional.of(
|
||||
(Supplier<Stream<HttpMessageReader<?>>>) messageReaders::stream));
|
||||
Configuration mockConfig = mock(Configuration.class);
|
||||
when(mockConfig.messageReaders()).thenReturn(messageReaders::stream);
|
||||
when(mockExchange.getAttribute(RoutingFunctions.CONFIGURATION_ATTRIBUTE))
|
||||
.thenReturn(Optional.of(mockConfig));
|
||||
|
||||
assertEquals(body, defaultRequest.body().stream());
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -218,11 +220,13 @@ public class DefaultResponseBuilderTests {
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
ServerWebExchange exchange =
|
||||
new DefaultServerWebExchange(request, response, new MockWebSessionManager());
|
||||
Set<HttpMessageWriter<?>>
|
||||
messageWriters = Collections
|
||||
.singleton(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
|
||||
exchange.getAttributes().put(Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE,
|
||||
(Supplier<Stream<HttpMessageWriter<?>>>) messageWriters::stream);
|
||||
|
||||
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
||||
messageWriters.add(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
|
||||
|
||||
Configuration mockConfig = mock(Configuration.class);
|
||||
when(mockConfig.messageWriters()).thenReturn(messageWriters::stream);
|
||||
exchange.getAttributes().put(RoutingFunctions.CONFIGURATION_ATTRIBUTE, mockConfig);
|
||||
|
||||
result.writeTo(exchange).block();
|
||||
assertNotNull(response.getBody());
|
||||
@@ -239,11 +243,13 @@ public class DefaultResponseBuilderTests {
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
ServerWebExchange exchange =
|
||||
new DefaultServerWebExchange(request, response, new MockWebSessionManager());
|
||||
Set<HttpMessageWriter<?>>
|
||||
messageWriters = Collections
|
||||
.singleton(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
|
||||
exchange.getAttributes().put(Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE,
|
||||
(Supplier<Stream<HttpMessageWriter<?>>>) messageWriters::stream);
|
||||
|
||||
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
||||
messageWriters.add(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
|
||||
|
||||
Configuration mockConfig = mock(Configuration.class);
|
||||
when(mockConfig.messageWriters()).thenReturn(messageWriters::stream);
|
||||
exchange.getAttributes().put(RoutingFunctions.CONFIGURATION_ATTRIBUTE, mockConfig);
|
||||
|
||||
result.writeTo(exchange).block();
|
||||
assertEquals(HttpStatus.NOT_ACCEPTABLE, response.getStatusCode());
|
||||
@@ -259,10 +265,13 @@ public class DefaultResponseBuilderTests {
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
ServerWebExchange exchange =
|
||||
new DefaultServerWebExchange(request, response, new MockWebSessionManager());
|
||||
Set<HttpMessageWriter<?>> messageWriters = Collections
|
||||
.singleton(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
|
||||
exchange.getAttributes().put(Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE,
|
||||
(Supplier<Stream<HttpMessageWriter<?>>>) messageWriters::stream);
|
||||
|
||||
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
||||
messageWriters.add(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
|
||||
|
||||
Configuration mockConfig = mock(Configuration.class);
|
||||
when(mockConfig.messageWriters()).thenReturn(messageWriters::stream);
|
||||
exchange.getAttributes().put(RoutingFunctions.CONFIGURATION_ATTRIBUTE, mockConfig);
|
||||
|
||||
result.writeTo(exchange).block();
|
||||
assertNotNull(response.getBody());
|
||||
@@ -311,10 +320,13 @@ public class DefaultResponseBuilderTests {
|
||||
View view = mock(View.class);
|
||||
when(viewResolver.resolveViewName("view", Locale.ENGLISH)).thenReturn(Mono.just(view));
|
||||
when(view.render(model, null, exchange)).thenReturn(Mono.empty());
|
||||
exchange.getAttributes().put(Router.VIEW_RESOLVERS_ATTRIBUTE,
|
||||
(Supplier<Stream<ViewResolver>>) () -> Collections
|
||||
.singleton(viewResolver).stream());
|
||||
|
||||
List<ViewResolver> viewResolvers = new ArrayList<>();
|
||||
viewResolvers.add(viewResolver);
|
||||
|
||||
Configuration mockConfig = mock(Configuration.class);
|
||||
when(mockConfig.viewResolvers()).thenReturn(viewResolvers::stream);
|
||||
exchange.getAttributes().put(RoutingFunctions.CONFIGURATION_ATTRIBUTE, mockConfig);
|
||||
|
||||
result.writeTo(exchange).block();
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.web.reactive.function.Router.route;
|
||||
import static org.springframework.web.reactive.function.RoutingFunctions.route;
|
||||
|
||||
/**
|
||||
* Tests the use of {@link HandlerFunction} and {@link RoutingFunction} in a
|
||||
@@ -119,8 +119,8 @@ public class DispatcherHandlerIntegrationTests extends AbstractHttpHandlerIntegr
|
||||
@Bean
|
||||
public HandlerMapping handlerMapping(RoutingFunction<?> routingFunction,
|
||||
ApplicationContext applicationContext) {
|
||||
return Router.toHandlerMapping(routingFunction,
|
||||
new Router.Configuration() {
|
||||
return RoutingFunctions.toHandlerMapping(routingFunction,
|
||||
new org.springframework.web.reactive.function.Configuration() {
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
|
||||
return () -> getMessageReaders().stream();
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.web.reactive.function.RequestPredicates.GET;
|
||||
import static org.springframework.web.reactive.function.RequestPredicates.POST;
|
||||
import static org.springframework.web.reactive.function.Router.route;
|
||||
import static org.springframework.web.reactive.function.RoutingFunctions.route;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
|
||||
@@ -57,7 +57,7 @@ public class RouterTests {
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(true);
|
||||
|
||||
RoutingFunction<Void> result = Router.route(requestPredicate, handlerFunction);
|
||||
RoutingFunction<Void> result = RoutingFunctions.route(requestPredicate, handlerFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
@@ -73,7 +73,7 @@ public class RouterTests {
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(false);
|
||||
|
||||
RoutingFunction<Void> result = Router.route(requestPredicate, handlerFunction);
|
||||
RoutingFunction<Void> result = RoutingFunctions.route(requestPredicate, handlerFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
@@ -89,7 +89,7 @@ public class RouterTests {
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(true);
|
||||
|
||||
RoutingFunction<Void> result = Router.subroute(requestPredicate, routingFunction);
|
||||
RoutingFunction<Void> result = RoutingFunctions.subroute(requestPredicate, routingFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
@@ -106,7 +106,7 @@ public class RouterTests {
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(false);
|
||||
|
||||
RoutingFunction<Void> result = Router.subroute(requestPredicate, routingFunction);
|
||||
RoutingFunction<Void> result = RoutingFunctions.subroute(requestPredicate, routingFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
@@ -128,7 +128,7 @@ public class RouterTests {
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(false);
|
||||
|
||||
Router.Configuration configuration = mock(Router.Configuration.class);
|
||||
Configuration configuration = mock(Configuration.class);
|
||||
when(configuration.messageReaders()).thenReturn(
|
||||
() -> Collections.<HttpMessageReader<?>>emptyList().stream());
|
||||
when(configuration.messageWriters()).thenReturn(
|
||||
@@ -136,72 +136,13 @@ public class RouterTests {
|
||||
when(configuration.viewResolvers()).thenReturn(
|
||||
() -> Collections.<ViewResolver>emptyList().stream());
|
||||
|
||||
HttpHandler result = Router.toHttpHandler(routingFunction, configuration);
|
||||
HttpHandler result = RoutingFunctions.toHttpHandler(routingFunction, configuration);
|
||||
assertNotNull(result);
|
||||
|
||||
MockServerHttpRequest httpRequest = new MockServerHttpRequest(HttpMethod.GET, "http://localhost");
|
||||
MockServerHttpRequest httpRequest =
|
||||
new MockServerHttpRequest(HttpMethod.GET, "http://localhost");
|
||||
MockServerHttpResponse serverHttpResponse = new MockServerHttpResponse();
|
||||
result.handle(httpRequest, serverHttpResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toConfiguration() throws Exception {
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.registerSingleton("messageWriter", DummyMessageWriter.class);
|
||||
applicationContext.registerSingleton("messageReader", DummyMessageReader.class);
|
||||
applicationContext.refresh();
|
||||
|
||||
Router.Configuration configuration = Router.toConfiguration(applicationContext);
|
||||
assertTrue(configuration.messageReaders().get()
|
||||
.allMatch(r -> r instanceof DummyMessageReader));
|
||||
assertTrue(configuration.messageWriters().get()
|
||||
.allMatch(r -> r instanceof DummyMessageWriter));
|
||||
|
||||
}
|
||||
|
||||
private static class DummyMessageWriter implements HttpMessageWriter<Object> {
|
||||
|
||||
@Override
|
||||
public boolean canWrite(ResolvableType elementType, MediaType mediaType, Map<String, Object> hints) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getWritableMediaTypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> write(Publisher<?> inputStream, ResolvableType elementType,
|
||||
MediaType mediaType,
|
||||
ReactiveHttpOutputMessage outputMessage,
|
||||
Map<String, Object> hints) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyMessageReader implements HttpMessageReader<Object> {
|
||||
|
||||
@Override
|
||||
public boolean canRead(ResolvableType elementType, MediaType mediaType, Map<String, Object> hints) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getReadableMediaTypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Object> read(ResolvableType elementType, ReactiveHttpInputMessage inputMessage,
|
||||
Map<String, Object> hints) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> readMono(ResolvableType elementType, ReactiveHttpInputMessage inputMessage,
|
||||
Map<String, Object> hints) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.web.client.reactive.WebClient;
|
||||
|
||||
import static org.springframework.web.client.reactive.ClientWebRequestBuilders.get;
|
||||
import static org.springframework.web.client.reactive.ResponseExtractors.bodyStream;
|
||||
import static org.springframework.web.reactive.function.Router.route;
|
||||
import static org.springframework.web.reactive.function.RoutingFunctions.route;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
|
||||
Reference in New Issue
Block a user