Add Web Reactive Java config
This commit is contained in:
parent
505569c992
commit
03b474edfe
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* 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.config;
|
||||||
|
|
||||||
|
import org.springframework.util.PathMatcher;
|
||||||
|
import org.springframework.web.util.HttpRequestPathHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assist with configuring {@code HandlerMapping}'s with path matching options.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class PathMatchConfigurer {
|
||||||
|
|
||||||
|
private Boolean suffixPatternMatch;
|
||||||
|
|
||||||
|
private Boolean trailingSlashMatch;
|
||||||
|
|
||||||
|
private Boolean registeredSuffixPatternMatch;
|
||||||
|
|
||||||
|
private HttpRequestPathHelper pathHelper;
|
||||||
|
|
||||||
|
private PathMatcher pathMatcher;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use suffix pattern match (".*") when matching patterns to
|
||||||
|
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
|
||||||
|
* <p>By default this is set to {@code true}.
|
||||||
|
* @see #registeredSuffixPatternMatch
|
||||||
|
*/
|
||||||
|
public PathMatchConfigurer setUseSuffixPatternMatch(Boolean suffixPatternMatch) {
|
||||||
|
this.suffixPatternMatch = suffixPatternMatch;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to match to URLs irrespective of the presence of a trailing slash.
|
||||||
|
* If enabled a method mapped to "/users" also matches to "/users/".
|
||||||
|
* <p>The default value is {@code true}.
|
||||||
|
*/
|
||||||
|
public PathMatchConfigurer setUseTrailingSlashMatch(Boolean trailingSlashMatch) {
|
||||||
|
this.trailingSlashMatch = trailingSlashMatch;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether suffix pattern matching should work only against path extensions
|
||||||
|
* that are explicitly registered. This is generally recommended to reduce
|
||||||
|
* ambiguity and to avoid issues such as when a "." (dot) appears in the path
|
||||||
|
* for other reasons.
|
||||||
|
* <p>By default this is set to "true".
|
||||||
|
*/
|
||||||
|
public PathMatchConfigurer setUseRegisteredSuffixPatternMatch(Boolean registeredSuffixPatternMatch) {
|
||||||
|
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a {@code HttpRequestPathHelper} for the resolution of lookup paths.
|
||||||
|
* <p>Default is {@code HttpRequestPathHelper}.
|
||||||
|
*/
|
||||||
|
public PathMatchConfigurer setPathHelper(HttpRequestPathHelper pathHelper) {
|
||||||
|
this.pathHelper = pathHelper;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the PathMatcher for matching URL paths against registered URL patterns.
|
||||||
|
* <p>Default is {@link org.springframework.util.AntPathMatcher AntPathMatcher}.
|
||||||
|
*/
|
||||||
|
public PathMatchConfigurer setPathMatcher(PathMatcher pathMatcher) {
|
||||||
|
this.pathMatcher = pathMatcher;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Boolean isUseSuffixPatternMatch() {
|
||||||
|
return this.suffixPatternMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Boolean isUseTrailingSlashMatch() {
|
||||||
|
return this.trailingSlashMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Boolean isUseRegisteredSuffixPatternMatch() {
|
||||||
|
return this.registeredSuffixPatternMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpRequestPathHelper getPathHelper() {
|
||||||
|
return this.pathHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PathMatcher getPathMatcher() {
|
||||||
|
return this.pathMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* 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.config;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.reactive.result.view.UrlBasedViewResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assist with configuring properties of a {@link UrlBasedViewResolver}.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class UrlBasedViewResolverRegistration {
|
||||||
|
|
||||||
|
private final UrlBasedViewResolver viewResolver;
|
||||||
|
|
||||||
|
|
||||||
|
public UrlBasedViewResolverRegistration(UrlBasedViewResolver viewResolver) {
|
||||||
|
Assert.notNull(viewResolver);
|
||||||
|
this.viewResolver = viewResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the prefix that gets prepended to view names when building a URL.
|
||||||
|
* @see UrlBasedViewResolver#setPrefix
|
||||||
|
*/
|
||||||
|
public UrlBasedViewResolverRegistration prefix(String prefix) {
|
||||||
|
this.viewResolver.setPrefix(prefix);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the suffix that gets appended to view names when building a URL.
|
||||||
|
* @see UrlBasedViewResolver#setSuffix
|
||||||
|
*/
|
||||||
|
public UrlBasedViewResolverRegistration suffix(String suffix) {
|
||||||
|
this.viewResolver.setSuffix(suffix);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the view class that should be used to create views.
|
||||||
|
* @see UrlBasedViewResolver#setViewClass
|
||||||
|
*/
|
||||||
|
public UrlBasedViewResolverRegistration viewClass(Class<?> viewClass) {
|
||||||
|
this.viewResolver.setViewClass(viewClass);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the view names (or name patterns) that can be handled by this view
|
||||||
|
* resolver. View names can contain simple wildcards such that 'my*', '*Report'
|
||||||
|
* and '*Repo*' will all match the view name 'myReport'.
|
||||||
|
* @see UrlBasedViewResolver#setViewNames
|
||||||
|
*/
|
||||||
|
public UrlBasedViewResolverRegistration viewNames(String... viewNames) {
|
||||||
|
this.viewResolver.setViewNames(viewNames);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UrlBasedViewResolver getViewResolver() {
|
||||||
|
return this.viewResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* 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.config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
|
import org.springframework.beans.factory.BeanInitializationException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.web.reactive.result.view.UrlBasedViewResolver;
|
||||||
|
import org.springframework.web.reactive.result.view.View;
|
||||||
|
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||||
|
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerConfigurer;
|
||||||
|
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerViewResolver;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assist with the configuration of a chain of {@link ViewResolver}'s supporting
|
||||||
|
* different template mechanisms.
|
||||||
|
*
|
||||||
|
* <p>In addition, you can also configure {@link #defaultViews(View...)
|
||||||
|
* defaultViews} for rendering according to the requested content type, e.g.
|
||||||
|
* JSON, XML, etc.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class ViewResolverRegistry {
|
||||||
|
|
||||||
|
private final List<ViewResolver> viewResolvers = new ArrayList<>(4);
|
||||||
|
|
||||||
|
private final List<View> defaultViews = new ArrayList<>(4);
|
||||||
|
|
||||||
|
private Integer order;
|
||||||
|
|
||||||
|
private final ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
|
||||||
|
public ViewResolverRegistry(ApplicationContext applicationContext) {
|
||||||
|
Assert.notNull(applicationContext);
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a {@code FreeMarkerViewResolver} with a ".ftl" suffix.
|
||||||
|
* <p><strong>Note</strong> that you must also configure FreeMarker by
|
||||||
|
* adding a {@link FreeMarkerConfigurer} bean.
|
||||||
|
*/
|
||||||
|
public UrlBasedViewResolverRegistration freeMarker() {
|
||||||
|
if (this.applicationContext != null && !hasBeanOfType(FreeMarkerConfigurer.class)) {
|
||||||
|
throw new BeanInitializationException("In addition to a FreeMarker view resolver " +
|
||||||
|
"there must also be a single FreeMarkerConfig bean in this web application context " +
|
||||||
|
"(or its parent): FreeMarkerConfigurer is the usual implementation. " +
|
||||||
|
"This bean may be given any name.");
|
||||||
|
}
|
||||||
|
FreeMarkerRegistration registration = new FreeMarkerRegistration();
|
||||||
|
UrlBasedViewResolver resolver = registration.getViewResolver();
|
||||||
|
resolver.setApplicationContext(this.applicationContext);
|
||||||
|
this.viewResolvers.add(resolver);
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hasBeanOfType(Class<?> beanType) {
|
||||||
|
return !ObjectUtils.isEmpty(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||||
|
this.applicationContext, beanType, false, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a {@link ViewResolver} bean instance. This may be useful to
|
||||||
|
* configure a 3rd party resolver implementation or as an alternative to
|
||||||
|
* other registration methods in this class when they don't expose some
|
||||||
|
* more advanced property that needs to be set.
|
||||||
|
*/
|
||||||
|
public void viewResolver(ViewResolver viewResolver) {
|
||||||
|
this.viewResolvers.add(viewResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default views associated with any view name and selected based on the
|
||||||
|
* best match for the requested content type.
|
||||||
|
* <p>Use {@link org.springframework.web.reactive.result.view.HttpMessageConverterView
|
||||||
|
* HttpMessageConverterView} to adapt and use any existing
|
||||||
|
* {@code HttpMessageConverter} (e.g. JSON, XML) as a {@code View}.
|
||||||
|
*/
|
||||||
|
public void defaultViews(View... defaultViews) {
|
||||||
|
this.defaultViews.addAll(Arrays.asList(defaultViews));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether any view resolvers have been registered.
|
||||||
|
*/
|
||||||
|
public boolean hasRegistrations() {
|
||||||
|
return (!this.viewResolvers.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the order for the
|
||||||
|
* {@link org.springframework.web.reactive.result.view.ViewResolutionResultHandler
|
||||||
|
* ViewResolutionResultHandler}.
|
||||||
|
* <p>By default this property is not set, which means the result handler is
|
||||||
|
* ordered at {@link Ordered#LOWEST_PRECEDENCE}.
|
||||||
|
*/
|
||||||
|
public void order(int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getOrder() {
|
||||||
|
return (this.order != null ? this.order : Ordered.LOWEST_PRECEDENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<ViewResolver> getViewResolvers() {
|
||||||
|
return this.viewResolvers;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<View> getDefaultViews() {
|
||||||
|
return this.defaultViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class FreeMarkerRegistration extends UrlBasedViewResolverRegistration {
|
||||||
|
|
||||||
|
public FreeMarkerRegistration() {
|
||||||
|
super(new FreeMarkerViewResolver());
|
||||||
|
getViewResolver().setSuffix(".ftl");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,316 @@
|
||||||
|
/*
|
||||||
|
* 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.config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import reactor.core.converter.DependencyUtils;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.codec.Decoder;
|
||||||
|
import org.springframework.core.codec.Encoder;
|
||||||
|
import org.springframework.core.codec.support.ByteBufferDecoder;
|
||||||
|
import org.springframework.core.codec.support.ByteBufferEncoder;
|
||||||
|
import org.springframework.core.codec.support.JacksonJsonDecoder;
|
||||||
|
import org.springframework.core.codec.support.JacksonJsonEncoder;
|
||||||
|
import org.springframework.core.codec.support.Jaxb2Decoder;
|
||||||
|
import org.springframework.core.codec.support.Jaxb2Encoder;
|
||||||
|
import org.springframework.core.codec.support.JsonObjectDecoder;
|
||||||
|
import org.springframework.core.codec.support.StringDecoder;
|
||||||
|
import org.springframework.core.codec.support.StringEncoder;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||||
|
import org.springframework.core.convert.support.GenericConversionService;
|
||||||
|
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
|
||||||
|
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
|
||||||
|
import org.springframework.format.Formatter;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||||
|
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
|
||||||
|
import org.springframework.web.reactive.result.SimpleHandlerAdapter;
|
||||||
|
import org.springframework.web.reactive.result.SimpleResultHandler;
|
||||||
|
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||||
|
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;
|
||||||
|
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;
|
||||||
|
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
||||||
|
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main class for Spring Web Reactive configuration.
|
||||||
|
*
|
||||||
|
* <p>Import directly or extend and override protected methods to customize.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
@Configuration @SuppressWarnings("unused")
|
||||||
|
public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private static final ClassLoader classLoader = WebReactiveConfiguration.class.getClassLoader();
|
||||||
|
|
||||||
|
private static final boolean jackson2Present =
|
||||||
|
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
|
||||||
|
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
|
||||||
|
|
||||||
|
private static final boolean jaxb2Present =
|
||||||
|
ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
|
||||||
|
|
||||||
|
|
||||||
|
private PathMatchConfigurer pathMatchConfigurer;
|
||||||
|
|
||||||
|
private List<HttpMessageConverter<?>> messageConverters;
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
|
||||||
|
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
|
||||||
|
mapping.setOrder(0);
|
||||||
|
mapping.setContentTypeResolver(mvcContentTypeResolver());
|
||||||
|
|
||||||
|
PathMatchConfigurer configurer = getPathMatchConfigurer();
|
||||||
|
if (configurer.isUseSuffixPatternMatch() != null) {
|
||||||
|
mapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
|
||||||
|
}
|
||||||
|
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
|
||||||
|
mapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
|
||||||
|
}
|
||||||
|
if (configurer.isUseTrailingSlashMatch() != null) {
|
||||||
|
mapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
|
||||||
|
}
|
||||||
|
if (configurer.getPathMatcher() != null) {
|
||||||
|
mapping.setPathMatcher(configurer.getPathMatcher());
|
||||||
|
}
|
||||||
|
if (configurer.getPathHelper() != null) {
|
||||||
|
mapping.setPathHelper(configurer.getPathHelper());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to plug a sub-class of {@link RequestMappingHandlerMapping}.
|
||||||
|
*/
|
||||||
|
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
|
||||||
|
return new RequestMappingHandlerMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RequestedContentTypeResolver mvcContentTypeResolver() {
|
||||||
|
RequestedContentTypeResolverBuilder builder = new RequestedContentTypeResolverBuilder();
|
||||||
|
builder.mediaTypes(getDefaultMediaTypeMappings());
|
||||||
|
configureRequestedContentTypeResolver(builder);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to configure media type mappings.
|
||||||
|
* @see RequestedContentTypeResolverBuilder#mediaTypes(Map)
|
||||||
|
*/
|
||||||
|
protected Map<String, MediaType> getDefaultMediaTypeMappings() {
|
||||||
|
Map<String, MediaType> map = new HashMap<>();
|
||||||
|
if (jackson2Present) {
|
||||||
|
map.put("json", MediaType.APPLICATION_JSON);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to configure how the requested content type is resolved.
|
||||||
|
*/
|
||||||
|
protected void configureRequestedContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for building the {@link PathMatchConfigurer}. This method is
|
||||||
|
* final, use {@link #configurePathMatching} to customize path matching.
|
||||||
|
*/
|
||||||
|
protected final PathMatchConfigurer getPathMatchConfigurer() {
|
||||||
|
if (this.pathMatchConfigurer == null) {
|
||||||
|
this.pathMatchConfigurer = new PathMatchConfigurer();
|
||||||
|
configurePathMatching(this.pathMatchConfigurer);
|
||||||
|
}
|
||||||
|
return this.pathMatchConfigurer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to configure path matching options.
|
||||||
|
*/
|
||||||
|
public void configurePathMatching(PathMatchConfigurer configurer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
|
||||||
|
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
|
||||||
|
|
||||||
|
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
|
||||||
|
addArgumentResolvers(resolvers);
|
||||||
|
if (!resolvers.isEmpty()) {
|
||||||
|
adapter.setCustomArgumentResolvers(resolvers);
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.setMessageConverters(getMessageConverters());
|
||||||
|
adapter.setConversionService(mvcConversionService());
|
||||||
|
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to plug a sub-class of {@link RequestMappingHandlerAdapter}.
|
||||||
|
*/
|
||||||
|
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
|
||||||
|
return new RequestMappingHandlerAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide custom argument resolvers without overriding the built-in ones.
|
||||||
|
*/
|
||||||
|
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main method to access message converters to use for decoding
|
||||||
|
* controller method arguments and encoding return values.
|
||||||
|
* <p>Use {@link #configureMessageConverters} to configure the list or
|
||||||
|
* {@link #extendMessageConverters} to add in addition to the default ones.
|
||||||
|
*/
|
||||||
|
protected final List<HttpMessageConverter<?>> getMessageConverters() {
|
||||||
|
if (this.messageConverters == null) {
|
||||||
|
this.messageConverters = new ArrayList<>();
|
||||||
|
configureMessageConverters(this.messageConverters);
|
||||||
|
if (this.messageConverters.isEmpty()) {
|
||||||
|
addDefaultHttpMessageConverters(this.messageConverters);
|
||||||
|
}
|
||||||
|
extendMessageConverters(this.messageConverters);
|
||||||
|
}
|
||||||
|
return this.messageConverters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to configure the message converters to use for decoding
|
||||||
|
* controller method arguments and encoding return values.
|
||||||
|
* <p>If no converters are specified, default will be added via
|
||||||
|
* {@link #addDefaultHttpMessageConverters}.
|
||||||
|
* @param converters a list to add converters to, initially an empty
|
||||||
|
*/
|
||||||
|
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds default converters that sub-classes can call from
|
||||||
|
* {@link #configureMessageConverters(List)}.
|
||||||
|
*/
|
||||||
|
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||||
|
converters.add(converter(new ByteBufferEncoder(), new ByteBufferDecoder()));
|
||||||
|
converters.add(converter(new StringEncoder(), new StringDecoder()));
|
||||||
|
converters.add(new ResourceHttpMessageConverter());
|
||||||
|
if (jaxb2Present) {
|
||||||
|
converters.add(converter(new Jaxb2Encoder(), new Jaxb2Decoder()));
|
||||||
|
}
|
||||||
|
if (jackson2Present) {
|
||||||
|
JsonObjectDecoder objectDecoder = new JsonObjectDecoder();
|
||||||
|
converters.add(converter(new JacksonJsonEncoder(), new JacksonJsonDecoder(objectDecoder)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> HttpMessageConverter<T> converter(Encoder<T> encoder, Decoder<T> decoder) {
|
||||||
|
return new CodecHttpMessageConverter<>(encoder, decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this to modify the list of converters after it has been
|
||||||
|
* configured, for example to add some in addition to the default ones.
|
||||||
|
*/
|
||||||
|
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: switch to DefaultFormattingConversionService
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public GenericConversionService mvcConversionService() {
|
||||||
|
GenericConversionService service = new GenericConversionService();
|
||||||
|
addFormatters(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: switch to FormatterRegistry
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to add custom {@link Converter}s and {@link Formatter}s.
|
||||||
|
* <p>By default this method method registers:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link ReactiveStreamsToCompletableFutureConverter}
|
||||||
|
* <li>{@link ReactiveStreamsToRxJava1Converter}
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
protected void addFormatters(ConverterRegistry registry) {
|
||||||
|
registry.addConverter(new ReactiveStreamsToCompletableFutureConverter());
|
||||||
|
if (DependencyUtils.hasRxJava1()) {
|
||||||
|
registry.addConverter(new ReactiveStreamsToRxJava1Converter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SimpleHandlerAdapter simpleHandlerAdapter() {
|
||||||
|
return new SimpleHandlerAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ResponseBodyResultHandler responseBodyResultHandler() {
|
||||||
|
return new ResponseBodyResultHandler(getMessageConverters(), mvcConversionService());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SimpleResultHandler simpleResultHandler() {
|
||||||
|
return new SimpleResultHandler(mvcConversionService());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ViewResolutionResultHandler viewResolutionResultHandler() {
|
||||||
|
ViewResolverRegistry registry = new ViewResolverRegistry(this.applicationContext);
|
||||||
|
configureViewResolvers(registry);
|
||||||
|
List<ViewResolver> resolvers = registry.getViewResolvers();
|
||||||
|
ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, mvcConversionService());
|
||||||
|
handler.setDefaultViews(registry.getDefaultViews());
|
||||||
|
handler.setOrder(registry.getOrder());
|
||||||
|
return handler;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this to configure view resolution.
|
||||||
|
*/
|
||||||
|
protected void configureViewResolvers(ViewResolverRegistry registry) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* Defines Spring Web Reactive configuration.
|
||||||
|
*/
|
||||||
|
package org.springframework.web.reactive.config;
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* 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.config;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.codec.support.JacksonJsonEncoder;
|
||||||
|
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||||
|
import org.springframework.web.reactive.result.view.HttpMessageConverterView;
|
||||||
|
import org.springframework.web.reactive.result.view.UrlBasedViewResolver;
|
||||||
|
import org.springframework.web.reactive.result.view.View;
|
||||||
|
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerConfigurer;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link ViewResolverRegistry}.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class ViewResolverRegistryTests {
|
||||||
|
|
||||||
|
private ViewResolverRegistry registry;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
StaticWebApplicationContext context = new StaticWebApplicationContext();
|
||||||
|
context.registerSingleton("freeMarkerConfigurer", FreeMarkerConfigurer.class);
|
||||||
|
this.registry = new ViewResolverRegistry(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void order() {
|
||||||
|
assertEquals(Ordered.LOWEST_PRECEDENCE, this.registry.getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasRegistrations() {
|
||||||
|
assertFalse(this.registry.hasRegistrations());
|
||||||
|
|
||||||
|
this.registry.freeMarker();
|
||||||
|
assertTrue(this.registry.hasRegistrations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noResolvers() {
|
||||||
|
assertNotNull(this.registry.getViewResolvers());
|
||||||
|
assertEquals(0, this.registry.getViewResolvers().size());
|
||||||
|
assertFalse(this.registry.hasRegistrations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customViewResolver() {
|
||||||
|
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
|
||||||
|
this.registry.viewResolver(viewResolver);
|
||||||
|
|
||||||
|
assertSame(viewResolver, this.registry.getViewResolvers().get(0));
|
||||||
|
assertEquals(1, this.registry.getViewResolvers().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void defaultViews() throws Exception {
|
||||||
|
View view = new HttpMessageConverterView(new JacksonJsonEncoder());
|
||||||
|
this.registry.defaultViews(view);
|
||||||
|
|
||||||
|
assertEquals(1, this.registry.getDefaultViews().size());
|
||||||
|
assertSame(view, this.registry.getDefaultViews().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,277 @@
|
||||||
|
/*
|
||||||
|
* 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.config;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import rx.Observable;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
|
import org.springframework.core.codec.support.JacksonJsonEncoder;
|
||||||
|
import org.springframework.core.codec.support.Jaxb2Decoder;
|
||||||
|
import org.springframework.core.codec.support.Jaxb2Encoder;
|
||||||
|
import org.springframework.core.codec.support.Pojo;
|
||||||
|
import org.springframework.core.codec.support.StringDecoder;
|
||||||
|
import org.springframework.core.codec.support.StringEncoder;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||||
|
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||||
|
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||||
|
import org.springframework.util.MimeType;
|
||||||
|
import org.springframework.util.MimeTypeUtils;
|
||||||
|
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||||
|
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;
|
||||||
|
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;
|
||||||
|
import org.springframework.web.reactive.result.view.HttpMessageConverterView;
|
||||||
|
import org.springframework.web.reactive.result.view.View;
|
||||||
|
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
||||||
|
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||||
|
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerConfigurer;
|
||||||
|
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerViewResolver;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||||
|
import org.springframework.web.server.session.WebSessionManager;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link WebReactiveConfiguration}.
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class WebReactiveConfigurationTests {
|
||||||
|
|
||||||
|
private MockServerHttpRequest request;
|
||||||
|
|
||||||
|
private ServerWebExchange exchange;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
|
||||||
|
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||||
|
this.exchange = new DefaultServerWebExchange(this.request, response, mock(WebSessionManager.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMappingHandlerMapping() throws Exception {
|
||||||
|
ApplicationContext context = loadConfig(WebReactiveConfiguration.class);
|
||||||
|
|
||||||
|
String name = "requestMappingHandlerMapping";
|
||||||
|
RequestMappingHandlerMapping mapping = context.getBean(name, RequestMappingHandlerMapping.class);
|
||||||
|
assertNotNull(mapping);
|
||||||
|
|
||||||
|
assertEquals(0, mapping.getOrder());
|
||||||
|
|
||||||
|
assertTrue(mapping.useSuffixPatternMatch());
|
||||||
|
assertTrue(mapping.useTrailingSlashMatch());
|
||||||
|
assertTrue(mapping.useRegisteredSuffixPatternMatch());
|
||||||
|
|
||||||
|
name = "mvcContentTypeResolver";
|
||||||
|
RequestedContentTypeResolver resolver = context.getBean(name, RequestedContentTypeResolver.class);
|
||||||
|
assertSame(resolver, mapping.getContentTypeResolver());
|
||||||
|
|
||||||
|
this.request.setUri(new URI("/path.json"));
|
||||||
|
List<MediaType> list = Collections.singletonList(MediaType.APPLICATION_JSON);
|
||||||
|
assertEquals(list, resolver.resolveMediaTypes(this.exchange));
|
||||||
|
|
||||||
|
this.request.setUri(new URI("/path.xml"));
|
||||||
|
assertEquals(Collections.emptyList(), resolver.resolveMediaTypes(this.exchange));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customPathMatchConfig() throws Exception {
|
||||||
|
ApplicationContext context = loadConfig(CustomPatchMatchConfig.class);
|
||||||
|
|
||||||
|
String name = "requestMappingHandlerMapping";
|
||||||
|
RequestMappingHandlerMapping mapping = context.getBean(name, RequestMappingHandlerMapping.class);
|
||||||
|
assertNotNull(mapping);
|
||||||
|
|
||||||
|
assertFalse(mapping.useSuffixPatternMatch());
|
||||||
|
assertFalse(mapping.useTrailingSlashMatch());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMappingHandlerAdapter() throws Exception {
|
||||||
|
ApplicationContext context = loadConfig(WebReactiveConfiguration.class);
|
||||||
|
|
||||||
|
String name = "requestMappingHandlerAdapter";
|
||||||
|
RequestMappingHandlerAdapter adapter = context.getBean(name, RequestMappingHandlerAdapter.class);
|
||||||
|
assertNotNull(adapter);
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
|
||||||
|
assertEquals(5, converters.size());
|
||||||
|
|
||||||
|
assertHasConverter(converters, ByteBuffer.class, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
assertHasConverter(converters, String.class, MediaType.TEXT_PLAIN);
|
||||||
|
assertHasConverter(converters, Resource.class, MediaType.IMAGE_PNG);
|
||||||
|
assertHasConverter(converters, Pojo.class, MediaType.APPLICATION_XML);
|
||||||
|
assertHasConverter(converters, Pojo.class, MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
|
name = "mvcConversionService";
|
||||||
|
ConversionService service = context.getBean(name, ConversionService.class);
|
||||||
|
assertSame(service, adapter.getConversionService());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customMessageConverterConfig() throws Exception {
|
||||||
|
ApplicationContext context = loadConfig(CustomMessageConverterConfig.class);
|
||||||
|
|
||||||
|
String name = "requestMappingHandlerAdapter";
|
||||||
|
RequestMappingHandlerAdapter adapter = context.getBean(name, RequestMappingHandlerAdapter.class);
|
||||||
|
assertNotNull(adapter);
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
|
||||||
|
assertEquals(2, converters.size());
|
||||||
|
|
||||||
|
assertHasConverter(converters, String.class, MediaType.TEXT_PLAIN);
|
||||||
|
assertHasConverter(converters, Pojo.class, MediaType.APPLICATION_XML);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mvcConversionService() throws Exception {
|
||||||
|
ApplicationContext context = loadConfig(WebReactiveConfiguration.class);
|
||||||
|
|
||||||
|
String name = "mvcConversionService";
|
||||||
|
ConversionService service = context.getBean(name, ConversionService.class);
|
||||||
|
assertNotNull(service);
|
||||||
|
|
||||||
|
service.canConvert(CompletableFuture.class, Mono.class);
|
||||||
|
service.canConvert(Observable.class, Flux.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void responseBodyResultHandler() throws Exception {
|
||||||
|
ApplicationContext context = loadConfig(WebReactiveConfiguration.class);
|
||||||
|
|
||||||
|
String name = "responseBodyResultHandler";
|
||||||
|
ResponseBodyResultHandler handler = context.getBean(name, ResponseBodyResultHandler.class);
|
||||||
|
assertNotNull(handler);
|
||||||
|
|
||||||
|
assertEquals(0, handler.getOrder());
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> converters = handler.getMessageConverters();
|
||||||
|
assertEquals(5, converters.size());
|
||||||
|
|
||||||
|
assertHasConverter(converters, ByteBuffer.class, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
|
assertHasConverter(converters, String.class, MediaType.TEXT_PLAIN);
|
||||||
|
assertHasConverter(converters, Resource.class, MediaType.IMAGE_PNG);
|
||||||
|
assertHasConverter(converters, Pojo.class, MediaType.APPLICATION_XML);
|
||||||
|
assertHasConverter(converters, Pojo.class, MediaType.APPLICATION_JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void viewResolutionResultHandler() throws Exception {
|
||||||
|
ApplicationContext context = loadConfig(CustomViewResolverConfig.class);
|
||||||
|
|
||||||
|
String name = "viewResolutionResultHandler";
|
||||||
|
ViewResolutionResultHandler handler = context.getBean(name, ViewResolutionResultHandler.class);
|
||||||
|
assertNotNull(handler);
|
||||||
|
|
||||||
|
assertEquals(Ordered.LOWEST_PRECEDENCE, handler.getOrder());
|
||||||
|
|
||||||
|
List<ViewResolver> resolvers = handler.getViewResolvers();
|
||||||
|
assertEquals(1, resolvers.size());
|
||||||
|
assertEquals(FreeMarkerViewResolver.class, resolvers.get(0).getClass());
|
||||||
|
|
||||||
|
List<View> views = handler.getDefaultViews();
|
||||||
|
assertEquals(1, views.size());
|
||||||
|
|
||||||
|
MimeType type = MimeTypeUtils.parseMimeType("application/json;charset=UTF-8");
|
||||||
|
assertEquals(type, views.get(0).getSupportedMediaTypes().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void assertHasConverter(List<HttpMessageConverter<?>> converters, Class<?> clazz, MediaType mediaType) {
|
||||||
|
ResolvableType type = ResolvableType.forClass(clazz);
|
||||||
|
assertTrue(converters.stream()
|
||||||
|
.filter(c -> c.canRead(type, mediaType) && c.canWrite(type, mediaType))
|
||||||
|
.findAny()
|
||||||
|
.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApplicationContext loadConfig(Class<?>... configurationClasses) {
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||||
|
context.register(configurationClasses);
|
||||||
|
context.refresh();
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class CustomPatchMatchConfig extends WebReactiveConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configurePathMatching(PathMatchConfigurer configurer) {
|
||||||
|
configurer.setUseSuffixPatternMatch(false);
|
||||||
|
configurer.setUseTrailingSlashMatch(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class CustomMessageConverterConfig extends WebReactiveConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||||
|
converters.add(new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||||
|
converters.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder(), new Jaxb2Decoder()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration @SuppressWarnings("unused")
|
||||||
|
static class CustomViewResolverConfig extends WebReactiveConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureViewResolvers(ViewResolverRegistry registry) {
|
||||||
|
registry.freeMarker();
|
||||||
|
registry.defaultViews(new HttpMessageConverterView(new JacksonJsonEncoder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FreeMarkerConfigurer freeMarkerConfig() {
|
||||||
|
return new FreeMarkerConfigurer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,6 @@ import java.nio.ByteBuffer;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
@ -40,19 +39,7 @@ import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.codec.support.ByteBufferDecoder;
|
|
||||||
import org.springframework.core.codec.support.ByteBufferEncoder;
|
|
||||||
import org.springframework.core.codec.support.JacksonJsonDecoder;
|
|
||||||
import org.springframework.core.codec.support.JacksonJsonEncoder;
|
import org.springframework.core.codec.support.JacksonJsonEncoder;
|
||||||
import org.springframework.core.codec.support.Jaxb2Decoder;
|
|
||||||
import org.springframework.core.codec.support.Jaxb2Encoder;
|
|
||||||
import org.springframework.core.codec.support.JsonObjectDecoder;
|
|
||||||
import org.springframework.core.codec.support.StringDecoder;
|
|
||||||
import org.springframework.core.codec.support.StringEncoder;
|
|
||||||
import org.springframework.core.convert.ConversionService;
|
|
||||||
import org.springframework.core.convert.support.GenericConversionService;
|
|
||||||
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
|
|
||||||
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
@ -62,9 +49,6 @@ import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.RequestEntity;
|
import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
|
||||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
|
||||||
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
|
|
||||||
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
|
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
|
||||||
import org.springframework.http.server.reactive.HttpHandler;
|
import org.springframework.http.server.reactive.HttpHandler;
|
||||||
import org.springframework.http.server.reactive.ZeroCopyIntegrationTests;
|
import org.springframework.http.server.reactive.ZeroCopyIntegrationTests;
|
||||||
|
@ -78,11 +62,9 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.reactive.DispatcherHandler;
|
import org.springframework.web.reactive.DispatcherHandler;
|
||||||
import org.springframework.web.reactive.result.SimpleResultHandler;
|
import org.springframework.web.reactive.config.ViewResolverRegistry;
|
||||||
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
import org.springframework.web.reactive.config.WebReactiveConfiguration;
|
||||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
|
||||||
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerConfigurer;
|
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerConfigurer;
|
||||||
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerViewResolver;
|
|
||||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
@ -385,61 +367,11 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
static class FrameworkConfig {
|
static class FrameworkConfig extends WebReactiveConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Override
|
||||||
public RequestMappingHandlerMapping handlerMapping() {
|
protected void configureViewResolvers(ViewResolverRegistry registry) {
|
||||||
return new RequestMappingHandlerMapping();
|
registry.freeMarker();
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public RequestMappingHandlerAdapter handlerAdapter() {
|
|
||||||
RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
|
|
||||||
handlerAdapter.setMessageConverters(getDefaultMessageConverters());
|
|
||||||
handlerAdapter.setConversionService(conversionService());
|
|
||||||
return handlerAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<HttpMessageConverter<?>> getDefaultMessageConverters() {
|
|
||||||
return Arrays.asList(
|
|
||||||
new CodecHttpMessageConverter<>(new ByteBufferEncoder(), new ByteBufferDecoder()),
|
|
||||||
new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder()),
|
|
||||||
new CodecHttpMessageConverter<>(new Jaxb2Encoder(), new Jaxb2Decoder()),
|
|
||||||
new CodecHttpMessageConverter<>(new JacksonJsonEncoder(),
|
|
||||||
new JacksonJsonDecoder(new JsonObjectDecoder())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ConversionService conversionService() {
|
|
||||||
// TODO: test failures with DefaultConversionService
|
|
||||||
GenericConversionService service = new GenericConversionService();
|
|
||||||
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
|
|
||||||
service.addConverter(new ReactiveStreamsToRxJava1Converter());
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ResponseBodyResultHandler responseBodyResultHandler() {
|
|
||||||
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
|
||||||
converters.add(new ResourceHttpMessageConverter());
|
|
||||||
converters.addAll(getDefaultMessageConverters());
|
|
||||||
return new ResponseBodyResultHandler(converters, conversionService());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SimpleResultHandler simpleHandlerResultHandler() {
|
|
||||||
return new SimpleResultHandler(conversionService());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ViewResolutionResultHandler viewResolverResultHandler() {
|
|
||||||
List<ViewResolver> resolvers = Collections.singletonList(freeMarkerViewResolver());
|
|
||||||
return new ViewResolutionResultHandler(resolvers, conversionService());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ViewResolver freeMarkerViewResolver() {
|
|
||||||
return new FreeMarkerViewResolver("", ".ftl");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
Loading…
Reference in New Issue