Consistent use of @Nullable across the codebase (even for internals)
Beyond just formally declaring the current behavior, this revision actually enforces non-null behavior in selected signatures now, not tolerating null values anymore when not explicitly documented. It also changes some utility methods with historic null-in/null-out tolerance towards enforced non-null return values, making them a proper citizen in non-null assignments. Some issues are left as to-do: in particular a thorough revision of spring-test, and a few tests with unclear failures (ignored as "TODO: NULLABLE") to be sorted out in a follow-up commit. Issue: SPR-15540
This commit is contained in:
@@ -63,7 +63,7 @@ public class HandlerResult {
|
||||
* @param context the binding context used for request handling
|
||||
*/
|
||||
public HandlerResult(Object handler, @Nullable Object returnValue, MethodParameter returnType,
|
||||
BindingContext context) {
|
||||
@Nullable BindingContext context) {
|
||||
|
||||
Assert.notNull(handler, "'handler' is required");
|
||||
Assert.notNull(returnType, "'returnType' is required");
|
||||
|
||||
@@ -53,7 +53,7 @@ public abstract class AbstractMappingContentTypeResolver implements MappingConte
|
||||
/**
|
||||
* Create an instance with the given map of file extensions and media types.
|
||||
*/
|
||||
public AbstractMappingContentTypeResolver(Map<String, MediaType> mediaTypes) {
|
||||
public AbstractMappingContentTypeResolver(@Nullable Map<String, MediaType> mediaTypes) {
|
||||
if (mediaTypes != null) {
|
||||
for (Map.Entry<String, MediaType> entry : mediaTypes.entrySet()) {
|
||||
String extension = entry.getKey().toLowerCase(Locale.ENGLISH);
|
||||
@@ -90,11 +90,8 @@ public abstract class AbstractMappingContentTypeResolver implements MappingConte
|
||||
// RequestedContentTypeResolver implementation
|
||||
|
||||
@Override
|
||||
public List<MediaType> resolveMediaTypes(ServerWebExchange exchange)
|
||||
throws NotAcceptableStatusException {
|
||||
|
||||
String key = extractKey(exchange);
|
||||
return resolveMediaTypes(key);
|
||||
public List<MediaType> resolveMediaTypes(ServerWebExchange exchange) throws NotAcceptableStatusException {
|
||||
return resolveMediaTypes(extractKey(exchange));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +100,7 @@ public abstract class AbstractMappingContentTypeResolver implements MappingConte
|
||||
* @return a list of resolved media types or an empty list
|
||||
* @throws NotAcceptableStatusException
|
||||
*/
|
||||
public List<MediaType> resolveMediaTypes(String key) throws NotAcceptableStatusException {
|
||||
public List<MediaType> resolveMediaTypes(@Nullable String key) throws NotAcceptableStatusException {
|
||||
if (StringUtils.hasText(key)) {
|
||||
MediaType mediaType = getMediaType(key);
|
||||
if (mediaType != null) {
|
||||
|
||||
@@ -48,13 +48,6 @@ public class PathExtensionContentTypeResolver extends AbstractMappingContentType
|
||||
private boolean ignoreUnknownExtensions = true;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance with the given map of file extensions and media types.
|
||||
*/
|
||||
public PathExtensionContentTypeResolver(Map<String, MediaType> mediaTypes) {
|
||||
super(mediaTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance without any mappings to start with. Mappings may be added
|
||||
* later on if any extensions are resolved through the Java Activation framework.
|
||||
@@ -63,6 +56,13 @@ public class PathExtensionContentTypeResolver extends AbstractMappingContentType
|
||||
super(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance with the given map of file extensions and media types.
|
||||
*/
|
||||
public PathExtensionContentTypeResolver(@Nullable Map<String, MediaType> mediaTypes) {
|
||||
super(mediaTypes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether to only use the registered mappings to look up file extensions, or also refer to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.accept;
|
||||
|
||||
import java.util.List;
|
||||
@@ -33,13 +34,10 @@ public interface RequestedContentTypeResolver {
|
||||
/**
|
||||
* Resolve the given request to a list of requested media types. The returned
|
||||
* list is ordered by specificity first and by quality parameter second.
|
||||
*
|
||||
* @param exchange the current exchange
|
||||
* @return the requested media types or an empty list
|
||||
*
|
||||
* @throws NotAcceptableStatusException if the requested media types is invalid
|
||||
*/
|
||||
List<MediaType> resolveMediaTypes(ServerWebExchange exchange)
|
||||
throws NotAcceptableStatusException;
|
||||
List<MediaType> resolveMediaTypes(ServerWebExchange exchange) throws NotAcceptableStatusException;
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.reactive.config;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
import org.springframework.web.util.pattern.ParsingPathMatcher;
|
||||
@@ -90,26 +91,29 @@ public class PathMatchConfigurer {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Boolean isUseSuffixPatternMatch() {
|
||||
return this.suffixPatternMatch;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Boolean isUseTrailingSlashMatch() {
|
||||
return this.trailingSlashMatch;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Boolean isUseRegisteredSuffixPatternMatch() {
|
||||
return this.registeredSuffixPatternMatch;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected HttpRequestPathHelper getPathHelper() {
|
||||
return this.pathHelper;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PathMatcher getPathMatcher() {
|
||||
if(this.pathMatcher != null
|
||||
&& this.pathMatcher.getClass().isAssignableFrom(ParsingPathMatcher.class)
|
||||
&& (this.trailingSlashMatch || this.suffixPatternMatch)) {
|
||||
if (this.pathMatcher instanceof ParsingPathMatcher && (this.trailingSlashMatch || this.suffixPatternMatch)) {
|
||||
throw new IllegalStateException("When using a ParsingPathMatcher, useTrailingSlashMatch" +
|
||||
" and useSuffixPatternMatch should be set to 'false'.");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.reactive.resource.CachingResourceResolver;
|
||||
@@ -63,7 +64,7 @@ public class ResourceChainRegistration {
|
||||
this(cacheResources, cacheResources ? new ConcurrentMapCache(DEFAULT_CACHE_NAME) : null);
|
||||
}
|
||||
|
||||
public ResourceChainRegistration(boolean cacheResources, Cache cache) {
|
||||
public ResourceChainRegistration(boolean cacheResources, @Nullable Cache cache) {
|
||||
Assert.isTrue(!cacheResources || cache != null, "'cache' is required when cacheResources=true");
|
||||
if (cacheResources) {
|
||||
this.resolvers.add(new CachingResourceResolver(cache));
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ResourceHandlerRegistry {
|
||||
* @param contentTypeResolver the content type resolver to use
|
||||
*/
|
||||
public ResourceHandlerRegistry(ApplicationContext applicationContext,
|
||||
CompositeContentTypeResolver contentTypeResolver) {
|
||||
@Nullable CompositeContentTypeResolver contentTypeResolver) {
|
||||
|
||||
Assert.notNull(applicationContext, "ApplicationContext is required");
|
||||
this.applicationContext = applicationContext;
|
||||
|
||||
@@ -75,7 +75,9 @@ public class ViewResolverRegistry {
|
||||
}
|
||||
FreeMarkerRegistration registration = new FreeMarkerRegistration();
|
||||
UrlBasedViewResolver resolver = registration.getViewResolver();
|
||||
resolver.setApplicationContext(this.applicationContext);
|
||||
if (this.applicationContext != null) {
|
||||
resolver.setApplicationContext(this.applicationContext);
|
||||
}
|
||||
this.viewResolvers.add(resolver);
|
||||
return registration;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.validation.Validator;
|
||||
@@ -63,6 +64,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebExceptionHandler;
|
||||
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
|
||||
import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
|
||||
/**
|
||||
* The main class for Spring WebFlux configuration.
|
||||
@@ -119,20 +121,26 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
mapping.setCorsConfigurations(getCorsConfigurations());
|
||||
|
||||
PathMatchConfigurer configurer = getPathMatchConfigurer();
|
||||
if (configurer.isUseSuffixPatternMatch() != null) {
|
||||
mapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
|
||||
Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
|
||||
Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
|
||||
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
|
||||
if (useSuffixPatternMatch != null) {
|
||||
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
|
||||
}
|
||||
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
|
||||
mapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
|
||||
if (useRegisteredSuffixPatternMatch != null) {
|
||||
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
|
||||
}
|
||||
if (configurer.isUseTrailingSlashMatch() != null) {
|
||||
mapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
|
||||
if (useTrailingSlashMatch != null) {
|
||||
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
|
||||
}
|
||||
if (configurer.getPathMatcher() != null) {
|
||||
mapping.setPathMatcher(configurer.getPathMatcher());
|
||||
|
||||
HttpRequestPathHelper pathHelper = configurer.getPathHelper();
|
||||
if (pathHelper != null) {
|
||||
mapping.setPathHelper(pathHelper);
|
||||
}
|
||||
if (configurer.getPathHelper() != null) {
|
||||
mapping.setPathHelper(configurer.getPathHelper());
|
||||
PathMatcher pathMatcher = configurer.getPathMatcher();
|
||||
if (pathMatcher != null) {
|
||||
mapping.setPathMatcher(pathMatcher);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
@@ -313,7 +321,10 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
|
||||
initializer.setConversionService(webFluxConversionService());
|
||||
initializer.setValidator(webFluxValidator());
|
||||
initializer.setMessageCodesResolver(getMessageCodesResolver());
|
||||
MessageCodesResolver messageCodesResolver = getMessageCodesResolver();
|
||||
if (messageCodesResolver != null) {
|
||||
initializer.setMessageCodesResolver(messageCodesResolver);
|
||||
}
|
||||
return initializer;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,8 +132,7 @@ public abstract class BodyInserters {
|
||||
serverRequest.get(), (ServerHttpResponse) outputMessage, context.hints());
|
||||
}
|
||||
else {
|
||||
return messageWriter.write(inputStream, RESOURCE_TYPE, null,
|
||||
outputMessage, context.hints());
|
||||
return messageWriter.write(inputStream, RESOURCE_TYPE, null, outputMessage, context.hints());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ public class UnsupportedMediaTypeException extends NestedRuntimeException {
|
||||
/**
|
||||
* Constructor for when the Content-Type can be parsed but is not supported.
|
||||
*/
|
||||
public UnsupportedMediaTypeException(MediaType contentType, List<MediaType> supportedMediaTypes) {
|
||||
super("Content type '" + contentType + "' not supported");
|
||||
public UnsupportedMediaTypeException(@Nullable MediaType contentType, List<MediaType> supportedMediaTypes) {
|
||||
super("Content type '" + (contentType != null ? contentType : "") + "' not supported");
|
||||
this.contentType = contentType;
|
||||
this.supportedMediaTypes = Collections.unmodifiableList(supportedMediaTypes);
|
||||
}
|
||||
|
||||
@@ -73,9 +73,7 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder {
|
||||
|
||||
@Override
|
||||
public ClientRequest.Builder headers(HttpHeaders headers) {
|
||||
if (headers != null) {
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -87,15 +85,12 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder {
|
||||
|
||||
@Override
|
||||
public ClientRequest.Builder cookies(MultiValueMap<String, String> cookies) {
|
||||
if (cookies != null) {
|
||||
this.cookies.putAll(cookies);
|
||||
}
|
||||
this.cookies.putAll(cookies);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, P extends Publisher<S>> ClientRequest.Builder body(P publisher,
|
||||
Class<S> elementClass) {
|
||||
public <S, P extends Publisher<S>> ClientRequest.Builder body(P publisher, Class<S> elementClass) {
|
||||
Assert.notNull(publisher, "'publisher' must not be null");
|
||||
Assert.notNull(elementClass, "'elementClass' must not be null");
|
||||
|
||||
@@ -105,7 +100,7 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder {
|
||||
|
||||
@Override
|
||||
public ClientRequest.Builder body(BodyInserter<?, ? super ClientHttpRequest> inserter) {
|
||||
this.inserter = inserter != null ? inserter : BodyInserters.empty();
|
||||
this.inserter = inserter;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -67,8 +68,8 @@ class DefaultWebClient implements WebClient {
|
||||
private final MultiValueMap<String, String> defaultCookies;
|
||||
|
||||
|
||||
DefaultWebClient(ExchangeFunction exchangeFunction, UriBuilderFactory factory,
|
||||
HttpHeaders defaultHeaders, MultiValueMap<String, String> defaultCookies) {
|
||||
DefaultWebClient(ExchangeFunction exchangeFunction, @Nullable UriBuilderFactory factory,
|
||||
@Nullable HttpHeaders defaultHeaders, @Nullable MultiValueMap<String, String> defaultCookies) {
|
||||
|
||||
this.exchangeFunction = exchangeFunction;
|
||||
this.uriBuilderFactory = (factory != null ? factory : new DefaultUriBuilderFactory());
|
||||
@@ -205,9 +206,7 @@ class DefaultWebClient implements WebClient {
|
||||
|
||||
@Override
|
||||
public DefaultRequestBodySpec headers(HttpHeaders headers) {
|
||||
if (headers != null) {
|
||||
getHeaders().putAll(headers);
|
||||
}
|
||||
getHeaders().putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -243,9 +242,7 @@ class DefaultWebClient implements WebClient {
|
||||
|
||||
@Override
|
||||
public DefaultRequestBodySpec cookies(MultiValueMap<String, String> cookies) {
|
||||
if (cookies != null) {
|
||||
getCookies().putAll(cookies);
|
||||
}
|
||||
getCookies().putAll(cookies);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -298,10 +295,9 @@ class DefaultWebClient implements WebClient {
|
||||
return ClientRequest.method(this.httpMethod, this.uri).headers(initHeaders()).cookies(initCookies());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private HttpHeaders initHeaders() {
|
||||
if (CollectionUtils.isEmpty(defaultHeaders) && CollectionUtils.isEmpty(this.headers)) {
|
||||
return null;
|
||||
return new HttpHeaders();
|
||||
}
|
||||
else if (CollectionUtils.isEmpty(defaultHeaders)) {
|
||||
return this.headers;
|
||||
@@ -321,10 +317,9 @@ class DefaultWebClient implements WebClient {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MultiValueMap<String, String> initCookies() {
|
||||
if (CollectionUtils.isEmpty(defaultCookies) && CollectionUtils.isEmpty(this.cookies)) {
|
||||
return null;
|
||||
return new LinkedMultiValueMap<>(0);
|
||||
}
|
||||
else if (CollectionUtils.isEmpty(defaultCookies)) {
|
||||
return this.cookies;
|
||||
|
||||
@@ -84,9 +84,7 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
||||
|
||||
@Override
|
||||
public EntityResponse.Builder<T> headers(HttpHeaders headers) {
|
||||
if (headers != null) {
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -115,16 +113,14 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityResponse.Builder<T> eTag(String eTag) {
|
||||
if (eTag != null) {
|
||||
if (!eTag.startsWith("\"") && !eTag.startsWith("W/\"")) {
|
||||
eTag = "\"" + eTag;
|
||||
}
|
||||
if (!eTag.endsWith("\"")) {
|
||||
eTag = eTag + "\"";
|
||||
}
|
||||
public EntityResponse.Builder<T> eTag(String etag) {
|
||||
if (!etag.startsWith("\"") && !etag.startsWith("W/\"")) {
|
||||
etag = "\"" + etag;
|
||||
}
|
||||
this.headers.setETag(eTag);
|
||||
if (!etag.endsWith("\"")) {
|
||||
etag = etag + "\"";
|
||||
}
|
||||
this.headers.setETag(etag);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,25 +85,19 @@ class DefaultRenderingResponseBuilder implements RenderingResponse.Builder {
|
||||
|
||||
@Override
|
||||
public RenderingResponse.Builder modelAttributes(Object... attributes) {
|
||||
if (attributes != null) {
|
||||
modelAttributes(Arrays.asList(attributes));
|
||||
}
|
||||
modelAttributes(Arrays.asList(attributes));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderingResponse.Builder modelAttributes(Collection<?> attributes) {
|
||||
if (attributes != null) {
|
||||
attributes.forEach(this::modelAttribute);
|
||||
}
|
||||
attributes.forEach(this::modelAttribute);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderingResponse.Builder modelAttributes(Map<String, ?> attributes) {
|
||||
if (attributes != null) {
|
||||
this.model.putAll(attributes);
|
||||
}
|
||||
this.model.putAll(attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -117,9 +111,7 @@ class DefaultRenderingResponseBuilder implements RenderingResponse.Builder {
|
||||
|
||||
@Override
|
||||
public RenderingResponse.Builder headers(HttpHeaders headers) {
|
||||
if (headers != null) {
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,9 +75,7 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder {
|
||||
|
||||
@Override
|
||||
public ServerResponse.BodyBuilder headers(HttpHeaders headers) {
|
||||
if (headers != null) {
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -106,16 +104,14 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse.BodyBuilder eTag(String eTag) {
|
||||
if (eTag != null) {
|
||||
if (!eTag.startsWith("\"") && !eTag.startsWith("W/\"")) {
|
||||
eTag = "\"" + eTag;
|
||||
}
|
||||
if (!eTag.endsWith("\"")) {
|
||||
eTag = eTag + "\"";
|
||||
}
|
||||
public ServerResponse.BodyBuilder eTag(String etag) {
|
||||
if (!etag.startsWith("\"") && !etag.startsWith("W/\"")) {
|
||||
etag = "\"" + etag;
|
||||
}
|
||||
this.headers.setETag(eTag);
|
||||
if (!etag.endsWith("\"")) {
|
||||
etag = etag + "\"";
|
||||
}
|
||||
this.headers.setETag(etag);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.BodyExtractor;
|
||||
import org.springframework.web.server.WebSession;
|
||||
@@ -293,7 +294,7 @@ public abstract class RequestPredicates {
|
||||
}
|
||||
|
||||
|
||||
private static void traceMatch(String prefix, Object desired, Object actual, boolean match) {
|
||||
private static void traceMatch(String prefix, Object desired, @Nullable Object actual, boolean match) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
String message = String.format("%s \"%s\" %s against value \"%s\"",
|
||||
prefix, desired, match ? "matches" : "does not match", actual);
|
||||
|
||||
@@ -54,23 +54,25 @@ class ResourceHandlerFunction implements HandlerFunction<ServerResponse> {
|
||||
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||
switch (request.method()) {
|
||||
case GET:
|
||||
return EntityResponse.fromObject(this.resource).build()
|
||||
.map(response -> response);
|
||||
case HEAD:
|
||||
Resource headResource = new HeadMethodResource(this.resource);
|
||||
return EntityResponse.fromObject(headResource).build()
|
||||
.map(response -> response);
|
||||
case OPTIONS:
|
||||
return ServerResponse.ok()
|
||||
.allow(SUPPORTED_METHODS)
|
||||
.body(BodyInserters.empty());
|
||||
default:
|
||||
return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.allow(SUPPORTED_METHODS)
|
||||
.body(BodyInserters.empty());
|
||||
HttpMethod method = request.method();
|
||||
if (method != null) {
|
||||
switch (method) {
|
||||
case GET:
|
||||
return EntityResponse.fromObject(this.resource).build()
|
||||
.map(response -> response);
|
||||
case HEAD:
|
||||
Resource headResource = new HeadMethodResource(this.resource);
|
||||
return EntityResponse.fromObject(headResource).build()
|
||||
.map(response -> response);
|
||||
case OPTIONS:
|
||||
return ServerResponse.ok()
|
||||
.allow(SUPPORTED_METHODS)
|
||||
.body(BodyInserters.empty());
|
||||
}
|
||||
}
|
||||
return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.allow(SUPPORTED_METHODS)
|
||||
.body(BodyInserters.empty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.json.Jackson2CodecSupport;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.function.BodyExtractor;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebSession;
|
||||
@@ -54,6 +55,7 @@ public interface ServerRequest {
|
||||
/**
|
||||
* Return the HTTP method.
|
||||
*/
|
||||
@Nullable
|
||||
HttpMethod method();
|
||||
|
||||
/**
|
||||
@@ -241,6 +243,7 @@ public interface ServerRequest {
|
||||
* <p>If the header value does not contain a port, the returned
|
||||
* {@linkplain InetSocketAddress#getPort() port} will be {@code 0}.
|
||||
*/
|
||||
@Nullable
|
||||
InetSocketAddress host();
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,7 +105,7 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
|
||||
|
||||
private List<RouterFunction<?>> routerFunctions() {
|
||||
SortedRouterFunctionsContainer container = new SortedRouterFunctionsContainer();
|
||||
getApplicationContext().getAutowireCapableBeanFactory().autowireBean(container);
|
||||
obtainApplicationContext().getAutowireCapableBeanFactory().autowireBean(container);
|
||||
|
||||
return CollectionUtils.isEmpty(container.routerFunctions) ? Collections.emptyList() :
|
||||
container.routerFunctions;
|
||||
|
||||
@@ -188,7 +188,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
// Bean name or resolved handler?
|
||||
if (handler instanceof String) {
|
||||
String handlerName = (String) handler;
|
||||
handler = getApplicationContext().getBean(handlerName);
|
||||
handler = obtainApplicationContext().getBean(handlerName);
|
||||
}
|
||||
|
||||
validateHandler(handler, exchange);
|
||||
@@ -241,8 +241,8 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
// Eagerly resolve handler if referencing singleton via name.
|
||||
if (!this.lazyInitHandlers && handler instanceof String) {
|
||||
String handlerName = (String) handler;
|
||||
if (getApplicationContext().isSingleton(handlerName)) {
|
||||
resolvedHandler = getApplicationContext().getBean(handlerName);
|
||||
if (obtainApplicationContext().isSingleton(handlerName)) {
|
||||
resolvedHandler = obtainApplicationContext().getBean(handlerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -37,7 +38,7 @@ public abstract class AbstractResourceResolver implements ResourceResolver {
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Resource> resolveResource(ServerWebExchange exchange, String requestPath,
|
||||
public Mono<Resource> resolveResource(@Nullable ServerWebExchange exchange, String requestPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
@@ -58,7 +59,7 @@ public abstract class AbstractResourceResolver implements ResourceResolver {
|
||||
}
|
||||
|
||||
|
||||
protected abstract Mono<Resource> resolveResourceInternal(ServerWebExchange exchange,
|
||||
protected abstract Mono<Resource> resolveResourceInternal(@Nullable ServerWebExchange exchange,
|
||||
String requestPath, List<? extends Resource> locations, ResourceResolverChain chain);
|
||||
|
||||
protected abstract Mono<String> resolveUrlPathInternal(String resourceUrlPath,
|
||||
|
||||
@@ -201,7 +201,7 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
this.link = iniLinkFlag(line, this.cacheSection);
|
||||
}
|
||||
|
||||
private static boolean initCacheSectionFlag(String line, LineInfo previousLine) {
|
||||
private static boolean initCacheSectionFlag(String line, @Nullable LineInfo previousLine) {
|
||||
if (MANIFEST_SECTION_HEADERS.contains(line.trim())) {
|
||||
return line.trim().equals(CACHE_HEADER);
|
||||
}
|
||||
@@ -252,6 +252,7 @@ public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
return this.line;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Resource getResource() {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@@ -44,15 +45,19 @@ public class CachingResourceResolver extends AbstractResourceResolver {
|
||||
private final Cache cache;
|
||||
|
||||
|
||||
public CachingResourceResolver(CacheManager cacheManager, String cacheName) {
|
||||
this(cacheManager.getCache(cacheName));
|
||||
}
|
||||
|
||||
public CachingResourceResolver(Cache cache) {
|
||||
Assert.notNull(cache, "Cache is required");
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public CachingResourceResolver(CacheManager cacheManager, String cacheName) {
|
||||
Cache cache = cacheManager.getCache(cacheName);
|
||||
if (cache == null) {
|
||||
throw new IllegalArgumentException("Cache '" + cacheName + "' not found");
|
||||
}
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured {@code Cache}.
|
||||
@@ -63,8 +68,8 @@ public class CachingResourceResolver extends AbstractResourceResolver {
|
||||
|
||||
|
||||
@Override
|
||||
protected Mono<Resource> resolveResourceInternal(ServerWebExchange exchange, String requestPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
protected Mono<Resource> resolveResourceInternal(@Nullable ServerWebExchange exchange,
|
||||
String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
|
||||
String key = computeKey(exchange, requestPath);
|
||||
Resource cachedResource = this.cache.get(key, Resource.class);
|
||||
@@ -85,7 +90,7 @@ public class CachingResourceResolver extends AbstractResourceResolver {
|
||||
});
|
||||
}
|
||||
|
||||
protected String computeKey(ServerWebExchange exchange, String requestPath) {
|
||||
protected String computeKey(@Nullable ServerWebExchange exchange, String requestPath) {
|
||||
StringBuilder key = new StringBuilder(RESOLVED_RESOURCE_CACHE_KEY_PREFIX);
|
||||
key.append(requestPath);
|
||||
if (exchange != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -41,15 +41,19 @@ public class CachingResourceTransformer implements ResourceTransformer {
|
||||
private final Cache cache;
|
||||
|
||||
|
||||
public CachingResourceTransformer(CacheManager cacheManager, String cacheName) {
|
||||
this(cacheManager.getCache(cacheName));
|
||||
}
|
||||
|
||||
public CachingResourceTransformer(Cache cache) {
|
||||
Assert.notNull(cache, "Cache is required");
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public CachingResourceTransformer(CacheManager cacheManager, String cacheName) {
|
||||
Cache cache = cacheManager.getCache(cacheName);
|
||||
if (cache == null) {
|
||||
throw new IllegalArgumentException("Cache '" + cacheName + "' not found");
|
||||
}
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured {@code Cache}.
|
||||
|
||||
@@ -33,6 +33,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -293,7 +294,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class DefaultResourceResolverChain implements ResourceResolverChain {
|
||||
private int index = -1;
|
||||
|
||||
|
||||
public DefaultResourceResolverChain(List<? extends ResourceResolver> resolvers) {
|
||||
public DefaultResourceResolverChain(@Nullable List<? extends ResourceResolver> resolvers) {
|
||||
if (resolvers != null) {
|
||||
this.resolvers.addAll(resolvers);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ class DefaultResourceResolverChain implements ResourceResolverChain {
|
||||
|
||||
ResourceResolver resolver = getNext();
|
||||
if (resolver == null) {
|
||||
return null;
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -68,7 +68,7 @@ class DefaultResourceResolverChain implements ResourceResolverChain {
|
||||
public Mono<String> resolveUrlPath(String resourcePath, List<? extends Resource> locations) {
|
||||
ResourceResolver resolver = getNext();
|
||||
if (resolver == null) {
|
||||
return null;
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-201/ 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.
|
||||
@@ -43,7 +43,7 @@ class DefaultResourceTransformerChain implements ResourceTransformerChain {
|
||||
|
||||
|
||||
public DefaultResourceTransformerChain(ResourceResolverChain resolverChain,
|
||||
List<ResourceTransformer> transformers) {
|
||||
@Nullable List<ResourceTransformer> transformers) {
|
||||
|
||||
Assert.notNull(resolverChain, "ResourceResolverChain is required");
|
||||
this.resolverChain = resolverChain;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -28,6 +28,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -43,8 +44,8 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
public class GzipResourceResolver extends AbstractResourceResolver {
|
||||
|
||||
@Override
|
||||
protected Mono<Resource> resolveResourceInternal(ServerWebExchange exchange, String requestPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
protected Mono<Resource> resolveResourceInternal(@Nullable ServerWebExchange exchange,
|
||||
String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
|
||||
return chain.resolveResource(exchange, requestPath, locations)
|
||||
.map(resource -> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -27,6 +27,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@@ -64,6 +65,7 @@ public class PathResourceResolver extends AbstractResourceResolver {
|
||||
this.allowedLocations = locations;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Resource[] getAllowedLocations() {
|
||||
return this.allowedLocations;
|
||||
}
|
||||
@@ -113,10 +115,11 @@ public class PathResourceResolver extends AbstractResourceResolver {
|
||||
return Mono.just(resource);
|
||||
}
|
||||
else if (logger.isTraceEnabled()) {
|
||||
Resource[] allowedLocations = getAllowedLocations();
|
||||
logger.trace("Resource path=\"" + resourcePath + "\" was successfully resolved " +
|
||||
"but resource=\"" + resource.getURL() + "\" is neither under the " +
|
||||
"current location=\"" + location.getURL() + "\" nor under any of the " +
|
||||
"allowed locations=" + Arrays.asList(getAllowedLocations()));
|
||||
"allowed locations=" + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]"));
|
||||
}
|
||||
}
|
||||
else if (logger.isTraceEnabled()) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -44,7 +45,7 @@ public interface ResourceResolver {
|
||||
* @param chain the chain of remaining resolvers to delegate to
|
||||
* @return the resolved resource or an empty {@code Mono} if unresolved
|
||||
*/
|
||||
Mono<Resource> resolveResource(ServerWebExchange exchange, String requestPath,
|
||||
Mono<Resource> resolveResource(@Nullable ServerWebExchange exchange, String requestPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -39,7 +39,7 @@ public interface ResourceResolverChain {
|
||||
* @param exchange the current exchange
|
||||
* @param requestPath the portion of the request path to use
|
||||
* @param locations the locations to search in when looking up resources
|
||||
* @return the resolved resource or an empty {@code Mono} if unresolved
|
||||
* @return the resolved resource; or an empty {@code Mono} if unresolved
|
||||
*/
|
||||
Mono<Resource> resolveResource(@Nullable ServerWebExchange exchange, String requestPath,
|
||||
List<? extends Resource> locations);
|
||||
@@ -51,7 +51,7 @@ public interface ResourceResolverChain {
|
||||
* <p>This is useful when rendering URL links to clients.
|
||||
* @param resourcePath the internal resource path
|
||||
* @param locations the locations to search in when looking up resources
|
||||
* @return the resolved public URL path or an empty {@code Mono} if unresolved
|
||||
* @return the resolved public URL path; or an empty {@code Mono} if unresolved
|
||||
*/
|
||||
Mono<String> resolveUrlPath(String resourcePath, List<? extends Resource> locations);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.Collections;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@@ -50,8 +50,9 @@ public abstract class ResourceTransformerSupport implements ResourceTransformer
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the configured {@code ResourceUrlProvider}.
|
||||
* Return the configured {@code ResourceUrlProvider}.
|
||||
*/
|
||||
@Nullable
|
||||
public ResourceUrlProvider getResourceUrlProvider() {
|
||||
return this.resourceUrlProvider;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -101,7 +102,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||
* from the Spring {@code ApplicationContext}. However if this property is
|
||||
* used, the auto-detection is turned off.
|
||||
*/
|
||||
public void setHandlerMap(Map<String, ResourceWebHandler> handlerMap) {
|
||||
public void setHandlerMap(@Nullable Map<String, ResourceWebHandler> handlerMap) {
|
||||
if (handlerMap != null) {
|
||||
this.handlerMap.clear();
|
||||
this.handlerMap.putAll(handlerMap);
|
||||
|
||||
@@ -85,8 +85,7 @@ import org.springframework.web.server.WebHandler;
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ResourceWebHandler
|
||||
implements WebHandler, InitializingBean, SmartInitializingSingleton {
|
||||
public class ResourceWebHandler implements WebHandler, InitializingBean, SmartInitializingSingleton {
|
||||
|
||||
/** Set of supported HTTP methods */
|
||||
private static final Set<HttpMethod> SUPPORTED_METHODS = EnumSet.of(HttpMethod.GET, HttpMethod.HEAD);
|
||||
@@ -113,10 +112,11 @@ public class ResourceWebHandler
|
||||
* Set the {@code List} of {@code Resource} paths to use as sources
|
||||
* for serving static resources.
|
||||
*/
|
||||
public void setLocations(List<Resource> locations) {
|
||||
Assert.notNull(locations, "Locations list must not be null");
|
||||
public void setLocations(@Nullable List<Resource> locations) {
|
||||
this.locations.clear();
|
||||
this.locations.addAll(locations);
|
||||
if (locations != null) {
|
||||
this.locations.addAll(locations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +132,7 @@ public class ResourceWebHandler
|
||||
* <p>By default {@link PathResourceResolver} is configured. If using this property,
|
||||
* it is recommended to add {@link PathResourceResolver} as the last resolver.
|
||||
*/
|
||||
public void setResourceResolvers(List<ResourceResolver> resourceResolvers) {
|
||||
public void setResourceResolvers(@Nullable List<ResourceResolver> resourceResolvers) {
|
||||
this.resourceResolvers.clear();
|
||||
if (resourceResolvers != null) {
|
||||
this.resourceResolvers.addAll(resourceResolvers);
|
||||
@@ -150,7 +150,7 @@ public class ResourceWebHandler
|
||||
* Configure the list of {@link ResourceTransformer}s to use.
|
||||
* <p>By default no transformers are configured for use.
|
||||
*/
|
||||
public void setResourceTransformers(List<ResourceTransformer> resourceTransformers) {
|
||||
public void setResourceTransformers(@Nullable List<ResourceTransformer> resourceTransformers) {
|
||||
this.resourceTransformers.clear();
|
||||
if (resourceTransformers != null) {
|
||||
this.resourceTransformers.addAll(resourceTransformers);
|
||||
@@ -172,6 +172,11 @@ public class ResourceWebHandler
|
||||
this.cacheControl = cacheControl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link org.springframework.http.CacheControl} instance to build
|
||||
* the Cache-Control HTTP response header.
|
||||
*/
|
||||
@Nullable
|
||||
public CacheControl getCacheControl() {
|
||||
return this.cacheControl;
|
||||
}
|
||||
@@ -187,6 +192,7 @@ public class ResourceWebHandler
|
||||
/**
|
||||
* Return the configured resource message writer.
|
||||
*/
|
||||
@Nullable
|
||||
public ResourceHttpMessageWriter getResourceHttpMessageWriter() {
|
||||
return this.resourceHttpMessageWriter;
|
||||
}
|
||||
@@ -204,21 +210,18 @@ public class ResourceWebHandler
|
||||
/**
|
||||
* Return the configured {@link CompositeContentTypeResolver}.
|
||||
*/
|
||||
@Nullable
|
||||
public CompositeContentTypeResolver getContentTypeResolver() {
|
||||
return this.contentTypeResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {
|
||||
logger.warn("Locations list is empty. No resources will be served unless a " +
|
||||
"custom ResourceResolver is configured as an alternative to PathResourceResolver.");
|
||||
}
|
||||
if (this.resourceResolvers.isEmpty()) {
|
||||
this.resourceResolvers.add(new PathResourceResolver());
|
||||
}
|
||||
initAllowedLocations();
|
||||
if (this.resourceHttpMessageWriter == null) {
|
||||
if (getResourceHttpMessageWriter() == null) {
|
||||
this.resourceHttpMessageWriter = new ResourceHttpMessageWriter();
|
||||
}
|
||||
}
|
||||
@@ -230,6 +233,10 @@ public class ResourceWebHandler
|
||||
*/
|
||||
protected void initAllowedLocations() {
|
||||
if (CollectionUtils.isEmpty(this.locations)) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Locations list is empty. No resources will be served unless a " +
|
||||
"custom ResourceResolver is configured as an alternative to PathResourceResolver.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int i = getResourceResolvers().size() - 1; i >= 0; i--) {
|
||||
@@ -275,7 +282,6 @@ public class ResourceWebHandler
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
||||
|
||||
return getResource(exchange)
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
logger.trace("No matching resource found - returning 404");
|
||||
@@ -292,7 +298,8 @@ public class ResourceWebHandler
|
||||
// Supported methods and required session
|
||||
HttpMethod httpMethod = exchange.getRequest().getMethod();
|
||||
if (!SUPPORTED_METHODS.contains(httpMethod)) {
|
||||
return Mono.error(new MethodNotAllowedException(httpMethod, SUPPORTED_METHODS));
|
||||
return Mono.error(new MethodNotAllowedException(
|
||||
exchange.getRequest().getMethodValue(), SUPPORTED_METHODS));
|
||||
}
|
||||
|
||||
// Header phase
|
||||
@@ -332,7 +339,9 @@ public class ResourceWebHandler
|
||||
}
|
||||
|
||||
setHeaders(exchange, resource, mediaType);
|
||||
return this.resourceHttpMessageWriter.write(Mono.just(resource),
|
||||
ResourceHttpMessageWriter writer = getResourceHttpMessageWriter();
|
||||
Assert.state(writer != null, "No ResourceHttpMessageWriter");
|
||||
return writer.write(Mono.just(resource),
|
||||
null, ResolvableType.forClass(Resource.class), mediaType,
|
||||
exchange.getRequest(), exchange.getResponse(), Collections.emptyMap());
|
||||
}
|
||||
@@ -485,7 +494,7 @@ public class ResourceWebHandler
|
||||
* @param resource the identified resource (never {@code null})
|
||||
* @param mediaType the resource's media type (never {@code null})
|
||||
*/
|
||||
protected void setHeaders(ServerWebExchange exchange, Resource resource, MediaType mediaType)
|
||||
protected void setHeaders(ServerWebExchange exchange, Resource resource, @Nullable MediaType mediaType)
|
||||
throws IOException {
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
|
||||
@@ -156,16 +156,16 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
|
||||
|
||||
@Override
|
||||
protected Mono<Resource> resolveResourceInternal(ServerWebExchange exchange, String requestPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
protected Mono<Resource> resolveResourceInternal(@Nullable ServerWebExchange exchange,
|
||||
String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
|
||||
return chain.resolveResource(exchange, requestPath, locations)
|
||||
.switchIfEmpty(Mono.defer(() ->
|
||||
resolveVersionedResource(exchange, requestPath, locations, chain)));
|
||||
}
|
||||
|
||||
private Mono<Resource> resolveVersionedResource(ServerWebExchange exchange, String requestPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
private Mono<Resource> resolveVersionedResource(@Nullable ServerWebExchange exchange,
|
||||
String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
|
||||
VersionStrategy versionStrategy = getStrategyForPath(requestPath);
|
||||
if (versionStrategy == null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.web.reactive.resource;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An extension of {@link VersionPathStrategy} that adds a method
|
||||
@@ -35,7 +34,6 @@ public interface VersionStrategy extends VersionPathStrategy {
|
||||
* @param resource the resource to check
|
||||
* @return the version (never {@code null})
|
||||
*/
|
||||
@Nullable
|
||||
String getResourceVersion(Resource resource);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -72,8 +72,8 @@ public class WebJarsResourceResolver extends AbstractResourceResolver {
|
||||
|
||||
|
||||
@Override
|
||||
protected Mono<Resource> resolveResourceInternal(ServerWebExchange exchange, String requestPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
protected Mono<Resource> resolveResourceInternal(@Nullable ServerWebExchange exchange,
|
||||
String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
|
||||
return chain.resolveResource(exchange, requestPath, locations)
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.cors.reactive.CorsUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -112,7 +113,7 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
|
||||
}
|
||||
return this;
|
||||
}
|
||||
return matchRequestMethod(exchange.getRequest().getMethod().name());
|
||||
return matchRequestMethod(exchange.getRequest().getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,11 +126,10 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
|
||||
return this;
|
||||
}
|
||||
HttpMethod expectedMethod = request.getHeaders().getAccessControlRequestMethod();
|
||||
return matchRequestMethod(expectedMethod.name());
|
||||
return matchRequestMethod(expectedMethod);
|
||||
}
|
||||
|
||||
private RequestMethodsRequestCondition matchRequestMethod(String httpMethodValue) {
|
||||
HttpMethod httpMethod = HttpMethod.resolve(httpMethodValue);
|
||||
private RequestMethodsRequestCondition matchRequestMethod(@Nullable HttpMethod httpMethod) {
|
||||
if (httpMethod != null) {
|
||||
for (RequestMethod method : getMethods()) {
|
||||
if (httpMethod.matches(method.name())) {
|
||||
|
||||
@@ -158,13 +158,13 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
|
||||
}
|
||||
String[] beanNames = getApplicationContext().getBeanNamesForType(Object.class);
|
||||
String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class);
|
||||
|
||||
for (String beanName : beanNames) {
|
||||
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
|
||||
Class<?> beanType = null;
|
||||
try {
|
||||
beanType = getApplicationContext().getType(beanName);
|
||||
beanType = obtainApplicationContext().getType(beanName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
|
||||
@@ -186,19 +186,20 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
*/
|
||||
protected void detectHandlerMethods(final Object handler) {
|
||||
Class<?> handlerType = (handler instanceof String ?
|
||||
getApplicationContext().getType((String) handler) : handler.getClass());
|
||||
final Class<?> userType = ClassUtils.getUserClass(handlerType);
|
||||
obtainApplicationContext().getType((String) handler) : handler.getClass());
|
||||
|
||||
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
|
||||
(MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
|
||||
if (handlerType != null) {
|
||||
final Class<?> userType = ClassUtils.getUserClass(handlerType);
|
||||
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
|
||||
(MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
|
||||
}
|
||||
methods.forEach((key, mapping) -> {
|
||||
Method invocableMethod = AopUtils.selectInvocableMethod(key, userType);
|
||||
registerHandlerMethod(handler, invocableMethod, mapping);
|
||||
});
|
||||
}
|
||||
methods.forEach((key, mapping) -> {
|
||||
Method invocableMethod = AopUtils.selectInvocableMethod(key, userType);
|
||||
registerHandlerMethod(handler, invocableMethod, mapping);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,7 +226,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
if (handler instanceof String) {
|
||||
String beanName = (String) handler;
|
||||
handlerMethod = new HandlerMethod(beanName,
|
||||
getApplicationContext().getAutowireCapableBeanFactory(), method);
|
||||
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
|
||||
}
|
||||
else {
|
||||
handlerMethod = new HandlerMethod(handler, method);
|
||||
@@ -428,7 +429,6 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* @param exchange the current exchange
|
||||
* @return the comparator (never {@code null})
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract Comparator<T> getMappingComparator(ServerWebExchange exchange);
|
||||
|
||||
|
||||
@@ -462,6 +462,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* Return matches for the given URL path. Not thread-safe.
|
||||
* @see #acquireReadLock()
|
||||
*/
|
||||
@Nullable
|
||||
public List<T> getMappingsByUrl(String urlPath) {
|
||||
return this.urlLookup.get(urlPath);
|
||||
}
|
||||
@@ -572,7 +573,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
|
||||
private final List<String> directUrls;
|
||||
|
||||
public MappingRegistration(T mapping, HandlerMethod handlerMethod, List<String> directUrls) {
|
||||
public MappingRegistration(T mapping, HandlerMethod handlerMethod, @Nullable List<String> directUrls) {
|
||||
Assert.notNull(mapping, "Mapping must not be null");
|
||||
Assert.notNull(handlerMethod, "HandlerMethod must not be null");
|
||||
this.mapping = mapping;
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -194,7 +195,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
}
|
||||
|
||||
private IllegalStateException getArgumentError(String text, MethodParameter parameter, Throwable ex) {
|
||||
private IllegalStateException getArgumentError(String text, MethodParameter parameter, @Nullable Throwable ex) {
|
||||
return new IllegalStateException(getDetailedErrorMessage(text, parameter), ex);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -564,6 +564,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
* {@code registeredSuffixPatternMatch=true}, the extensions are obtained
|
||||
* from the configured {@code contentTypeResolver}.
|
||||
*/
|
||||
@Nullable
|
||||
public Set<String> getFileExtensions() {
|
||||
RequestedContentTypeResolver resolver = getContentTypeResolver();
|
||||
if (useRegisteredSuffixPatternMatch() && resolver != null) {
|
||||
@@ -582,6 +583,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
this.contentTypeResolver = resolver;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RequestedContentTypeResolver getContentTypeResolver() {
|
||||
return this.contentTypeResolver;
|
||||
}
|
||||
|
||||
@@ -105,8 +105,9 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho
|
||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
ResolvableType bodyType = ResolvableType.forMethodParameter(bodyParameter);
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(bodyType.resolve());
|
||||
ResolvableType elementType = (adapter != null ? bodyType.getGeneric(0) : bodyType);
|
||||
Class<?> resolvedType = bodyType.resolve();
|
||||
ReactiveAdapter adapter = (resolvedType != null ? getAdapterRegistry().getAdapter(resolvedType) : null);
|
||||
ResolvableType elementType = (adapter != null ? bodyType.getGeneric() : bodyType);
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
@@ -161,7 +162,7 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho
|
||||
}
|
||||
|
||||
private ServerWebInputException handleMissingBody(MethodParameter param) {
|
||||
return new ServerWebInputException("Request body is missing: " + param.getMethod().toGenericString());
|
||||
return new ServerWebInputException("Request body is missing: " + param.getExecutable().toGenericString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
import org.springframework.web.reactive.result.HandlerResultHandlerSupport;
|
||||
@@ -85,8 +87,7 @@ public abstract class AbstractMessageWriterResultHandler extends HandlerResultHa
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Mono<Void> writeBody(Object body, MethodParameter bodyParameter, ServerWebExchange exchange) {
|
||||
|
||||
protected Mono<Void> writeBody(@Nullable Object body, MethodParameter bodyParameter, ServerWebExchange exchange) {
|
||||
ResolvableType bodyType = ResolvableType.forMethodParameter(bodyParameter);
|
||||
Class<?> bodyClass = bodyType.resolve();
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(bodyClass, body);
|
||||
|
||||
@@ -152,6 +152,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
* Resolve the given annotation-specified value,
|
||||
* potentially containing placeholders and expressions.
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolveStringValue(String value) {
|
||||
if (this.configurableBeanFactory == null) {
|
||||
return value;
|
||||
@@ -177,7 +178,8 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
/**
|
||||
* Apply type conversion if necessary.
|
||||
*/
|
||||
private Object applyConversion(Object value, NamedValueInfo namedValueInfo, MethodParameter parameter,
|
||||
@Nullable
|
||||
private Object applyConversion(@Nullable Object value, NamedValueInfo namedValueInfo, MethodParameter parameter,
|
||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
WebDataBinder binder = bindingContext.createDataBinder(exchange, namedValueInfo.name);
|
||||
@@ -249,6 +251,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
* A {@code null} results in a {@code false} value for {@code boolean}s or
|
||||
* an exception for other primitives.
|
||||
*/
|
||||
@Nullable
|
||||
private Object handleNullValue(String name, @Nullable Object value, Class<?> paramType) {
|
||||
if (value == null) {
|
||||
if (Boolean.TYPE.equals(paramType)) {
|
||||
@@ -274,7 +277,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
*/
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
protected void handleResolvedValue(
|
||||
Object arg, String name, MethodParameter parameter, Model model, ServerWebExchange exchange) {
|
||||
@Nullable Object arg, String name, MethodParameter parameter, Model model, ServerWebExchange exchange) {
|
||||
}
|
||||
|
||||
|
||||
@@ -290,7 +293,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
public NamedValueInfo(String name, boolean required, String defaultValue) {
|
||||
public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) {
|
||||
this.name = name;
|
||||
this.required = required;
|
||||
this.defaultValue = defaultValue;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -52,7 +53,7 @@ import org.springframework.web.reactive.result.method.InvocableHandlerMethod;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod;
|
||||
|
||||
import static org.springframework.core.MethodIntrospector.selectMethods;
|
||||
import static org.springframework.core.MethodIntrospector.*;
|
||||
|
||||
/**
|
||||
* Package-private class to assist {@link RequestMappingHandlerAdapter} with
|
||||
@@ -163,7 +164,7 @@ class ControllerMethodResolver {
|
||||
registrar.addIfModelAttribute(() -> new ModelAttributeMethodArgumentResolver(reactiveRegistry, true));
|
||||
}
|
||||
|
||||
private void initControllerAdviceCaches(ApplicationContext applicationContext) {
|
||||
private void initControllerAdviceCaches(@Nullable ApplicationContext applicationContext) {
|
||||
if (applicationContext == null) {
|
||||
return;
|
||||
}
|
||||
@@ -176,25 +177,27 @@ class ControllerMethodResolver {
|
||||
|
||||
for (ControllerAdviceBean bean : beans) {
|
||||
Class<?> beanType = bean.getBeanType();
|
||||
Set<Method> attrMethods = selectMethods(beanType, ATTRIBUTE_METHODS);
|
||||
if (!attrMethods.isEmpty()) {
|
||||
this.modelAttributeAdviceCache.put(bean, attrMethods);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected @ModelAttribute methods in " + bean);
|
||||
if (beanType != null) {
|
||||
Set<Method> attrMethods = selectMethods(beanType, ATTRIBUTE_METHODS);
|
||||
if (!attrMethods.isEmpty()) {
|
||||
this.modelAttributeAdviceCache.put(bean, attrMethods);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected @ModelAttribute methods in " + bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<Method> binderMethods = selectMethods(beanType, BINDER_METHODS);
|
||||
if (!binderMethods.isEmpty()) {
|
||||
this.initBinderAdviceCache.put(bean, binderMethods);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected @InitBinder methods in " + bean);
|
||||
Set<Method> binderMethods = selectMethods(beanType, BINDER_METHODS);
|
||||
if (!binderMethods.isEmpty()) {
|
||||
this.initBinderAdviceCache.put(bean, binderMethods);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected @InitBinder methods in " + bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
|
||||
if (resolver.hasExceptionMappings()) {
|
||||
this.exceptionHandlerAdviceCache.put(bean, resolver);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected @ExceptionHandler methods in " + bean);
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
|
||||
if (resolver.hasExceptionMappings()) {
|
||||
this.exceptionHandlerAdviceCache.put(bean, resolver);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected @ExceptionHandler methods in " + bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -23,6 +23,7 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
@@ -57,8 +58,9 @@ public class CookieValueMethodArgumentResolver extends AbstractNamedValueSyncArg
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
CookieValue annotation = parameter.getParameterAnnotation(CookieValue.class);
|
||||
return new CookieValueNamedValueInfo(annotation);
|
||||
CookieValue ann = parameter.getParameterAnnotation(CookieValue.class);
|
||||
Assert.state(ann != null, "No CookieValue annotation");
|
||||
return new CookieValueNamedValueInfo(ann);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,7 +71,7 @@ public class CookieValueMethodArgumentResolver extends AbstractNamedValueSyncArg
|
||||
return Optional.ofNullable(cookie);
|
||||
}
|
||||
else if (cookie != null) {
|
||||
return Optional.ofNullable(cookie.getValue());
|
||||
return Optional.of(cookie.getValue());
|
||||
}
|
||||
else {
|
||||
return Optional.empty();
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.springframework.core.Conventions;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
@@ -80,18 +79,16 @@ public class ErrorsMethodArgumentResolver extends HandlerMethodArgumentResolverS
|
||||
"Errors argument must be immediately after a model attribute argument");
|
||||
|
||||
int index = parameter.getParameterIndex() - 1;
|
||||
MethodParameter attributeParam = new MethodParameter(parameter.getMethod(), index);
|
||||
|
||||
ResolvableType type = ResolvableType.forMethodParameter(attributeParam);
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type.resolve());
|
||||
MethodParameter attributeParam = MethodParameter.forExecutable(parameter.getExecutable(), index);
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(attributeParam.getParameterType());
|
||||
|
||||
Assert.isNull(adapter, "Errors/BindingResult cannot be used with an async model attribute. " +
|
||||
"Either declare the model attribute without the async wrapper type " +
|
||||
"or handle WebExchangeBindException through the async type.");
|
||||
|
||||
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||
if (annot != null && StringUtils.hasText(annot.value())) {
|
||||
return annot.value();
|
||||
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||
if (ann != null && StringUtils.hasText(ann.value())) {
|
||||
return ann.value();
|
||||
}
|
||||
return Conventions.getVariableNameForParameter(attributeParam);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -50,13 +51,14 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueSyn
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter param) {
|
||||
return checkAnnotatedParamNoReactiveWrapper(param, Value.class, (annot, type) -> true);
|
||||
return checkAnnotatedParamNoReactiveWrapper(param, Value.class, (ann, type) -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
Value annotation = parameter.getParameterAnnotation(Value.class);
|
||||
return new ExpressionValueNamedValueInfo(annotation);
|
||||
Value ann = parameter.getParameterAnnotation(Value.class);
|
||||
Assert.state(ann != null, "No Value annotation");
|
||||
return new ExpressionValueNamedValueInfo(ann);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebExchangeDataBinder;
|
||||
@@ -41,7 +43,7 @@ class InitBinderBindingContext extends BindingContext {
|
||||
private final BindingContext binderMethodContext;
|
||||
|
||||
|
||||
InitBinderBindingContext(WebBindingInitializer initializer,
|
||||
InitBinderBindingContext(@Nullable WebBindingInitializer initializer,
|
||||
List<SyncInvocableHandlerMethod> binderMethods) {
|
||||
|
||||
super(initializer);
|
||||
@@ -56,8 +58,9 @@ class InitBinderBindingContext extends BindingContext {
|
||||
|
||||
this.binderMethods.stream()
|
||||
.filter(binderMethod -> {
|
||||
InitBinder annotation = binderMethod.getMethodAnnotation(InitBinder.class);
|
||||
Collection<String> names = Arrays.asList(annotation.value());
|
||||
InitBinder ann = binderMethod.getMethodAnnotation(InitBinder.class);
|
||||
Assert.state(ann != null, "No InitBinder annotation");
|
||||
Collection<String> names = Arrays.asList(ann.value());
|
||||
return (names.size() == 0 || names.contains(dataBinder.getObjectName()));
|
||||
})
|
||||
.forEach(method -> invokeBinderMethod(dataBinder, exchange, method));
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -106,8 +107,9 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
MethodParameter parameter, BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
ResolvableType type = ResolvableType.forMethodParameter(parameter);
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type.resolve());
|
||||
ResolvableType valueType = (adapter != null ? type.getGeneric(0) : type);
|
||||
Class<?> resolvedType = type.resolve();
|
||||
ReactiveAdapter adapter = (resolvedType != null ? getAdapterRegistry().getAdapter(resolvedType) : null);
|
||||
ResolvableType valueType = (adapter != null ? type.getGeneric() : type);
|
||||
|
||||
Assert.state(adapter == null || !adapter.isMultiValue(),
|
||||
() -> getClass().getSimpleName() + " doesn't support multi-value reactive type wrapper: " +
|
||||
@@ -165,7 +167,9 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
}
|
||||
|
||||
if (attribute == null) {
|
||||
return createAttribute(attributeName, attributeType.getRawClass(), context, exchange);
|
||||
Class<?> attributeClass = attributeType.getRawClass();
|
||||
Assert.state(attributeClass != null, "No attribute class");
|
||||
return createAttribute(attributeName,attributeClass , context, exchange);
|
||||
}
|
||||
|
||||
ReactiveAdapter adapterFrom = getAdapterRegistry().getAdapter(null, attribute);
|
||||
@@ -178,6 +182,7 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object findAndRemoveReactiveAttribute(Model model, String attributeName) {
|
||||
return model.asMap().entrySet().stream()
|
||||
.filter(entry -> {
|
||||
@@ -236,7 +241,7 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
|
||||
private boolean hasErrorsArgument(MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
||||
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
|
||||
return (paramTypes.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ class ModelInitializer {
|
||||
|
||||
private String getAttributeName(MethodParameter param) {
|
||||
return Optional
|
||||
.ofNullable(AnnotatedElementUtils.findMergedAnnotation(param.getMethod(), ModelAttribute.class))
|
||||
.ofNullable(AnnotatedElementUtils.findMergedAnnotation(param.getAnnotatedElement(), ModelAttribute.class))
|
||||
.filter(ann -> StringUtils.hasText(ann.value()))
|
||||
.map(ModelAttribute::value)
|
||||
.orElse(Conventions.getVariableNameForParameter(param));
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
@@ -75,8 +76,9 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueSyncAr
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
PathVariable annotation = parameter.getParameterAnnotation(PathVariable.class);
|
||||
return new PathVariableNamedValueInfo(annotation);
|
||||
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
|
||||
Assert.state(ann != null, "No PathVariable annotation");
|
||||
return new PathVariableNamedValueInfo(ann);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +97,7 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueSyncAr
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void handleResolvedValue(
|
||||
Object arg, String name, MethodParameter parameter, Model model, ServerWebExchange exchange) {
|
||||
@Nullable Object arg, String name, MethodParameter parameter, Model model, ServerWebExchange exchange) {
|
||||
|
||||
// TODO: View.PATH_VARIABLES ?
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -21,6 +22,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -55,8 +57,9 @@ public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueSy
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
RequestAttribute annot = parameter.getParameterAnnotation(RequestAttribute.class);
|
||||
return new NamedValueInfo(annot.name(), annot.required(), ValueConstants.DEFAULT_NONE);
|
||||
RequestAttribute ann = parameter.getParameterAnnotation(RequestAttribute.class);
|
||||
Assert.state(ann != null, "No RequestAttribute annotation");
|
||||
return new NamedValueInfo(ann.name(), ann.required(), ValueConstants.DEFAULT_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,9 +23,9 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
|
||||
@@ -61,8 +61,9 @@ public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentRe
|
||||
public Mono<Object> resolveArgument(
|
||||
MethodParameter param, BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
RequestBody annotation = param.getParameterAnnotation(RequestBody.class);
|
||||
return readBody(param, annotation.required(), bindingContext, exchange);
|
||||
RequestBody ann = param.getParameterAnnotation(RequestBody.class);
|
||||
Assert.state(ann != null, "No RequestBody annotation");
|
||||
return readBody(param, ann.required(), bindingContext, exchange);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -25,6 +25,7 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
@@ -69,8 +70,9 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueSyncA
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
RequestHeader annotation = parameter.getParameterAnnotation(RequestHeader.class);
|
||||
return new RequestHeaderNamedValueInfo(annotation);
|
||||
RequestHeader ann = parameter.getParameterAnnotation(RequestHeader.class);
|
||||
Assert.state(ann != null, "No RequestHeader annotation");
|
||||
return new RequestHeaderNamedValueInfo(ann);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -153,6 +153,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
/**
|
||||
* Return the file extensions to use for suffix pattern matching.
|
||||
*/
|
||||
@Nullable
|
||||
public Set<String> getFileExtensions() {
|
||||
return this.config.getFileExtensions();
|
||||
}
|
||||
@@ -246,19 +247,20 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
* result of merging annotation attributes within an annotation hierarchy.
|
||||
*/
|
||||
protected RequestMappingInfo createRequestMappingInfo(
|
||||
RequestMapping requestMapping, RequestCondition<?> customCondition) {
|
||||
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
|
||||
|
||||
return RequestMappingInfo
|
||||
RequestMappingInfo.Builder builder = RequestMappingInfo
|
||||
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
|
||||
.methods(requestMapping.method())
|
||||
.params(requestMapping.params())
|
||||
.headers(requestMapping.headers())
|
||||
.consumes(requestMapping.consumes())
|
||||
.produces(requestMapping.produces())
|
||||
.mappingName(requestMapping.name())
|
||||
.customCondition(customCondition)
|
||||
.options(this.config)
|
||||
.build();
|
||||
.mappingName(requestMapping.name());
|
||||
if (customCondition != null) {
|
||||
builder.customCondition(customCondition);
|
||||
}
|
||||
return builder.options(this.config).build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,7 +303,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
return config.applyPermitDefaultValues();
|
||||
}
|
||||
|
||||
private void updateCorsConfig(CorsConfiguration config, CrossOrigin annotation) {
|
||||
private void updateCorsConfig(CorsConfiguration config, @Nullable CrossOrigin annotation) {
|
||||
if (annotation == null) {
|
||||
return;
|
||||
}
|
||||
@@ -336,7 +338,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
}
|
||||
|
||||
private String resolveCorsAnnotationValue(String value) {
|
||||
return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
|
||||
if (this.embeddedValueResolver != null) {
|
||||
String resolved = this.embeddedValueResolver.resolveStringValue(value);
|
||||
return (resolved != null ? resolved : "");
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
@@ -71,7 +72,7 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageReaderArgu
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class);
|
||||
boolean isRequired = requestPart == null || requestPart.required();
|
||||
boolean isRequired = (requestPart == null || requestPart.required());
|
||||
String name = getPartName(parameter, requestPart);
|
||||
|
||||
Flux<Part> partFlux = getPartValues(name, exchange);
|
||||
@@ -99,7 +100,7 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageReaderArgu
|
||||
});
|
||||
}
|
||||
|
||||
private String getPartName(MethodParameter methodParam, RequestPart requestPart) {
|
||||
private String getPartName(MethodParameter methodParam, @Nullable RequestPart requestPart) {
|
||||
String partName = (requestPart != null ? requestPart.name() : "");
|
||||
if (partName.isEmpty()) {
|
||||
partName = methodParam.getParameterName();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.HandlerResultHandler;
|
||||
@@ -83,11 +85,12 @@ public class ResponseEntityResultHandler extends AbstractMessageWriterResultHand
|
||||
}
|
||||
ReactiveAdapter adapter = getAdapter(result);
|
||||
return adapter != null && !adapter.isNoValue() &&
|
||||
isSupportedType(result.getReturnType().getGeneric(0).resolve(Object.class));
|
||||
isSupportedType(result.getReturnType().getGeneric().resolve(Object.class));
|
||||
}
|
||||
|
||||
private boolean isSupportedType(Class<?> clazz) {
|
||||
return (HttpEntity.class.isAssignableFrom(clazz) && !RequestEntity.class.isAssignableFrom(clazz));
|
||||
private boolean isSupportedType(@Nullable Class<?> clazz) {
|
||||
return (clazz != null && HttpEntity.class.isAssignableFrom(clazz) &&
|
||||
!RequestEntity.class.isAssignableFrom(clazz));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ServerWebExchangeArgumentResolver extends HandlerMethodArgumentReso
|
||||
return Optional.of(exchange.getResponse());
|
||||
}
|
||||
else if (HttpMethod.class == paramType) {
|
||||
return Optional.of(exchange.getRequest().getMethod());
|
||||
return Optional.ofNullable(exchange.getRequest().getMethod());
|
||||
}
|
||||
else {
|
||||
// should never happen...
|
||||
|
||||
@@ -23,6 +23,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.SessionAttribute;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -49,8 +50,9 @@ public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueAr
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
SessionAttribute annot = parameter.getParameterAnnotation(SessionAttribute.class);
|
||||
return new NamedValueInfo(annot.name(), annot.required(), ValueConstants.DEFAULT_NONE);
|
||||
SessionAttribute ann = parameter.getParameterAnnotation(SessionAttribute.class);
|
||||
Assert.state(ann != null, "No SessionAttribute annotation");
|
||||
return new NamedValueInfo(ann.name(), ann.required(), ValueConstants.DEFAULT_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -13,11 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.view;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract base class for URL-based views. Provides a consistent way of
|
||||
@@ -56,6 +58,7 @@ public abstract class AbstractUrlBasedView extends AbstractView implements Initi
|
||||
/**
|
||||
* Return the URL of the resource that this view wraps.
|
||||
*/
|
||||
@Nullable
|
||||
public String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -18,17 +18,21 @@ package org.springframework.web.reactive.result.view;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -77,7 +81,7 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||
* Set the supported media types for this view.
|
||||
* Default is "text/html;charset=UTF-8".
|
||||
*/
|
||||
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
|
||||
public void setSupportedMediaTypes(@Nullable List<MediaType> supportedMediaTypes) {
|
||||
Assert.notEmpty(supportedMediaTypes, "MediaType List must not be empty");
|
||||
this.mediaTypes.clear();
|
||||
if (supportedMediaTypes != null) {
|
||||
@@ -115,7 +119,7 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||
* Set the name of the RequestContext attribute for this view.
|
||||
* Default is none.
|
||||
*/
|
||||
public void setRequestContextAttribute(String requestContextAttribute) {
|
||||
public void setRequestContextAttribute(@Nullable String requestContextAttribute) {
|
||||
this.requestContextAttribute = requestContextAttribute;
|
||||
}
|
||||
|
||||
@@ -132,10 +136,24 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the ApplicationContext for actual use.
|
||||
* @return the ApplicationContext (never {@code null})
|
||||
* @throws IllegalStateException in case of no ApplicationContext set
|
||||
* @since 5.0
|
||||
*/
|
||||
protected final ApplicationContext obtainApplicationContext() {
|
||||
ApplicationContext applicationContext = getApplicationContext();
|
||||
Assert.state(applicationContext != null, "No ApplicationContext");
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the model to render.
|
||||
* @param model Map with name Strings as keys and corresponding model
|
||||
@@ -171,7 +189,7 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||
* <p>The default implementation creates a combined output Map that includes
|
||||
* model as well as static attributes with the former taking precedence.
|
||||
*/
|
||||
protected Mono<Map<String, Object>> getModelAttributes(Map<String, ?> model, ServerWebExchange exchange) {
|
||||
protected Mono<Map<String, Object>> getModelAttributes(@Nullable Map<String, ?> model, ServerWebExchange exchange) {
|
||||
int size = (model != null ? model.size() : 0);
|
||||
|
||||
Map<String, Object> attributes = new LinkedHashMap<>(size);
|
||||
@@ -241,7 +259,7 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||
* @see #setRequestContextAttribute
|
||||
*/
|
||||
protected RequestContext createRequestContext(ServerWebExchange exchange, Map<String, Object> model) {
|
||||
return new RequestContext(exchange, model, getApplicationContext(), getRequestDataValueProcessor());
|
||||
return new RequestContext(exchange, model, obtainApplicationContext(), getRequestDataValueProcessor());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,7 +287,7 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||
*@param exchange current exchange @return {@code Mono} to represent when and if rendering succeeds
|
||||
*/
|
||||
protected abstract Mono<Void> renderInternal(Map<String, Object> renderAttributes,
|
||||
MediaType contentType, ServerWebExchange exchange);
|
||||
@Nullable MediaType contentType, ServerWebExchange exchange);
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.Model;
|
||||
|
||||
/**
|
||||
@@ -43,7 +44,7 @@ class DefaultRendering implements Rendering {
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
DefaultRendering(Object view, Model model, HttpStatus status, HttpHeaders headers) {
|
||||
DefaultRendering(Object view, @Nullable Model model, HttpStatus status, @Nullable HttpHeaders headers) {
|
||||
this.view = view;
|
||||
this.model = (model != null ? model.asMap() : Collections.emptyMap());
|
||||
this.status = status;
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
|
||||
/**
|
||||
* {@code View} that writes model attribute(s) with an {@link HttpMessageWriter}.
|
||||
*
|
||||
@@ -97,7 +96,7 @@ public class HttpMessageWriterView implements View {
|
||||
* otherwise raise an {@link IllegalStateException}.
|
||||
* </ul>
|
||||
*/
|
||||
public void setModelKeys(Set<String> modelKeys) {
|
||||
public void setModelKeys(@Nullable Set<String> modelKeys) {
|
||||
this.modelKeys.clear();
|
||||
if (modelKeys != null) {
|
||||
this.modelKeys.addAll(modelKeys);
|
||||
@@ -122,7 +121,10 @@ public class HttpMessageWriterView implements View {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object getObjectToRender(Map<String, ?> model) {
|
||||
private Object getObjectToRender(@Nullable Map<String, ?> model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, ?> result = model.entrySet().stream()
|
||||
.filter(this::isMatch)
|
||||
@@ -155,7 +157,7 @@ public class HttpMessageWriterView implements View {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Mono<Void> write(T value, MediaType contentType, ServerWebExchange exchange) {
|
||||
private <T> Mono<Void> write(T value, @Nullable MediaType contentType, ServerWebExchange exchange) {
|
||||
Publisher<T> input = Mono.justOrEmpty(value);
|
||||
ResolvableType elementType = ResolvableType.forClass(value.getClass());
|
||||
return ((HttpMessageWriter<T>) this.writer).write(
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -120,6 +121,7 @@ public class RedirectView extends AbstractUrlBasedView {
|
||||
/**
|
||||
* Get the redirect status code to use.
|
||||
*/
|
||||
@Nullable
|
||||
public HttpStatus getStatusCode() {
|
||||
return this.statusCode;
|
||||
}
|
||||
@@ -197,12 +199,14 @@ public class RedirectView extends AbstractUrlBasedView {
|
||||
* RequestDataValueProcessor}.
|
||||
*/
|
||||
protected final String createTargetUrl(Map<String, Object> model, ServerWebExchange exchange) {
|
||||
String url = getUrl();
|
||||
Assert.state(url != null, "'url' not set");
|
||||
|
||||
StringBuilder targetUrl = new StringBuilder();
|
||||
if (isContextRelative() && getUrl().startsWith("/")) {
|
||||
if (isContextRelative() && url.startsWith("/")) {
|
||||
targetUrl.append(exchange.getRequest().getContextPath());
|
||||
}
|
||||
targetUrl.append(getUrl());
|
||||
targetUrl.append(url);
|
||||
|
||||
if (StringUtils.hasText(targetUrl)) {
|
||||
Map<String, String> uriVars = getCurrentUriVariables(exchange);
|
||||
@@ -296,7 +300,10 @@ public class RedirectView extends AbstractUrlBasedView {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeUrl(targetUrl));
|
||||
response.getHeaders().setLocation(URI.create(encodedURL));
|
||||
response.setStatusCode(getStatusCode());
|
||||
HttpStatus status = getStatusCode();
|
||||
if (status != null) {
|
||||
response.setStatusCode(status);
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.view;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -73,7 +74,7 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
public RequestContext(ServerWebExchange exchange, Map<String, Object> model, MessageSource messageSource,
|
||||
RequestDataValueProcessor dataValueProcessor) {
|
||||
@Nullable RequestDataValueProcessor dataValueProcessor) {
|
||||
|
||||
Assert.notNull(exchange, "'exchange' is required");
|
||||
Assert.notNull(model, "'model' is required");
|
||||
@@ -368,7 +369,11 @@ public class RequestContext {
|
||||
Errors errors = this.errorsMap.get(name);
|
||||
if (errors == null) {
|
||||
errors = getModelObject(BindingResult.MODEL_KEY_PREFIX + name);
|
||||
if (errors == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (errors instanceof BindException) {
|
||||
errors = ((BindException) errors).getBindingResult();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
/**
|
||||
@@ -87,8 +88,8 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
* which by default is AbstractUrlBasedView.
|
||||
*/
|
||||
public void setViewClass(Class<?> viewClass) {
|
||||
if (viewClass == null || !requiredViewClass().isAssignableFrom(viewClass)) {
|
||||
String name = (viewClass != null ? viewClass.getName() : null);
|
||||
if (!requiredViewClass().isAssignableFrom(viewClass)) {
|
||||
String name = viewClass.getName();
|
||||
throw new IllegalArgumentException("Given view class [" + name + "] " +
|
||||
"is not of type [" + requiredViewClass().getName() + "]");
|
||||
}
|
||||
@@ -98,6 +99,7 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
/**
|
||||
* Return the view class to be used to create views.
|
||||
*/
|
||||
@Nullable
|
||||
protected Class<?> getViewClass() {
|
||||
return this.viewClass;
|
||||
}
|
||||
@@ -114,7 +116,7 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
/**
|
||||
* Set the prefix that gets prepended to view names when building a URL.
|
||||
*/
|
||||
public void setPrefix(String prefix) {
|
||||
public void setPrefix(@Nullable String prefix) {
|
||||
this.prefix = (prefix != null ? prefix : "");
|
||||
}
|
||||
|
||||
@@ -128,7 +130,7 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
/**
|
||||
* Set the suffix that gets appended to view names when building a URL.
|
||||
*/
|
||||
public void setSuffix(String suffix) {
|
||||
public void setSuffix(@Nullable String suffix) {
|
||||
this.suffix = (suffix != null ? suffix : "");
|
||||
}
|
||||
|
||||
@@ -153,6 +155,7 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
* Return the view names (or name patterns) that can be handled by this
|
||||
* {@link ViewResolver}.
|
||||
*/
|
||||
@Nullable
|
||||
protected String[] getViewNames() {
|
||||
return this.viewNames;
|
||||
}
|
||||
@@ -243,11 +246,15 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
|
||||
* @return the View instance
|
||||
*/
|
||||
protected AbstractUrlBasedView createUrlBasedView(String viewName) {
|
||||
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
|
||||
Class<?> viewClass = getViewClass();
|
||||
Assert.state(viewClass != null, "No view class");
|
||||
|
||||
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
|
||||
view.setSupportedMediaTypes(getSupportedMediaTypes());
|
||||
view.setRequestContextAttribute(getRequestContextAttribute());
|
||||
view.setDefaultCharset(getDefaultCharset());
|
||||
view.setUrl(getPrefix() + viewName + getSuffix());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
@@ -129,7 +130,7 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport
|
||||
* Set the default views to consider always when resolving view names and
|
||||
* trying to satisfy the best matching content type.
|
||||
*/
|
||||
public void setDefaultViews(List<View> defaultViews) {
|
||||
public void setDefaultViews(@Nullable List<View> defaultViews) {
|
||||
this.defaultViews.clear();
|
||||
if (defaultViews != null) {
|
||||
this.defaultViews.addAll(defaultViews);
|
||||
@@ -158,10 +159,11 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport
|
||||
type = result.getReturnType().getGeneric().resolve(Object.class);
|
||||
}
|
||||
|
||||
return (CharSequence.class.isAssignableFrom(type) || Rendering.class.isAssignableFrom(type) ||
|
||||
Model.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type) ||
|
||||
void.class.equals(type) || View.class.isAssignableFrom(type) ||
|
||||
!BeanUtils.isSimpleProperty(type));
|
||||
return (type != null &&
|
||||
(CharSequence.class.isAssignableFrom(type) || Rendering.class.isAssignableFrom(type) ||
|
||||
Model.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type) ||
|
||||
void.class.equals(type) || View.class.isAssignableFrom(type) ||
|
||||
!BeanUtils.isSimpleProperty(type)));
|
||||
}
|
||||
|
||||
private boolean hasModelAnnotation(MethodParameter parameter) {
|
||||
@@ -299,10 +301,10 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isBindingCandidate(String name, Object value) {
|
||||
return !name.startsWith(BindingResult.MODEL_KEY_PREFIX) && value != null &&
|
||||
private boolean isBindingCandidate(String name, @Nullable Object value) {
|
||||
return (!name.startsWith(BindingResult.MODEL_KEY_PREFIX) && value != null &&
|
||||
!value.getClass().isArray() && !(value instanceof Collection) &&
|
||||
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass());
|
||||
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
|
||||
}
|
||||
|
||||
private Mono<? extends Void> render(List<View> views, Map<String, Object> model,
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -56,7 +57,7 @@ public abstract class ViewResolverSupport implements ApplicationContextAware, Or
|
||||
* Set the supported media types for this view.
|
||||
* Default is "text/html;charset=UTF-8".
|
||||
*/
|
||||
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
|
||||
public void setSupportedMediaTypes(@Nullable List<MediaType> supportedMediaTypes) {
|
||||
Assert.notEmpty(supportedMediaTypes, "MediaType List must not be empty");
|
||||
this.mediaTypes.clear();
|
||||
if (supportedMediaTypes != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -39,9 +39,12 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.web.reactive.result.view.AbstractUrlBasedView;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -83,10 +86,23 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
||||
/**
|
||||
* Return the FreeMarker configuration used by this view.
|
||||
*/
|
||||
@Nullable
|
||||
protected Configuration getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the FreeMarker configuration for actual use.
|
||||
* @return the FreeMarker configuration (never {@code null})
|
||||
* @throws IllegalStateException in case of no Configuration object set
|
||||
* @since 5.0
|
||||
*/
|
||||
protected Configuration obtainConfiguration() {
|
||||
Configuration configuration = getConfiguration();
|
||||
Assert.state(configuration != null, "No Configuration set");
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encoding of the FreeMarker template file.
|
||||
* <p>By default {@link FreeMarkerConfigurer} sets the default encoding in
|
||||
@@ -101,6 +117,7 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
||||
/**
|
||||
* Return the encoding for the FreeMarker template.
|
||||
*/
|
||||
@Nullable
|
||||
protected String getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
@@ -124,7 +141,7 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
||||
protected FreeMarkerConfig autodetectConfiguration() throws BeansException {
|
||||
try {
|
||||
return BeanFactoryUtils.beanOfTypeIncludingAncestors(
|
||||
getApplicationContext(), FreeMarkerConfig.class, true, false);
|
||||
obtainApplicationContext(), FreeMarkerConfig.class, true, false);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new ApplicationContextException(
|
||||
@@ -164,8 +181,8 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Void> renderInternal(Map<String, Object> renderAttributes, MediaType contentType,
|
||||
ServerWebExchange exchange) {
|
||||
protected Mono<Void> renderInternal(Map<String, Object> renderAttributes,
|
||||
@Nullable MediaType contentType, ServerWebExchange exchange) {
|
||||
|
||||
// Expose all standard FreeMarker hash models.
|
||||
SimpleHash freeMarkerModel = getTemplateModel(renderAttributes, exchange);
|
||||
@@ -192,7 +209,7 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
||||
return exchange.getResponse().writeWith(Flux.just(dataBuffer));
|
||||
}
|
||||
|
||||
private Charset getCharset(MediaType mediaType) {
|
||||
private Charset getCharset(@Nullable MediaType mediaType) {
|
||||
return Optional.ofNullable(mediaType).map(MimeType::getCharset).orElse(getDefaultCharset());
|
||||
}
|
||||
|
||||
@@ -215,7 +232,7 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
||||
* @see freemarker.template.Configuration#getObjectWrapper()
|
||||
*/
|
||||
protected ObjectWrapper getObjectWrapper() {
|
||||
ObjectWrapper ow = getConfiguration().getObjectWrapper();
|
||||
ObjectWrapper ow = obtainConfiguration().getObjectWrapper();
|
||||
Version version = Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS;
|
||||
return (ow != null ? ow : new DefaultObjectWrapperBuilder(version).build());
|
||||
}
|
||||
@@ -230,8 +247,8 @@ public class FreeMarkerView extends AbstractUrlBasedView {
|
||||
*/
|
||||
protected Template getTemplate(Locale locale) throws IOException {
|
||||
return (getEncoding() != null ?
|
||||
getConfiguration().getTemplate(getUrl(), locale, getEncoding()) :
|
||||
getConfiguration().getTemplate(getUrl(), locale));
|
||||
obtainConfiguration().getTemplate(getUrl(), locale, getEncoding()) :
|
||||
obtainConfiguration().getTemplate(getUrl(), locale));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -19,6 +19,8 @@ package org.springframework.web.reactive.result.view.script;
|
||||
import java.nio.charset.Charset;
|
||||
import javax.script.ScriptEngine;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by objects that configure and manage a
|
||||
* JSR-223 {@link ScriptEngine} for automatic lookup in a web environment.
|
||||
@@ -32,42 +34,50 @@ public interface ScriptTemplateConfig {
|
||||
/**
|
||||
* Return the {@link ScriptEngine} to use by the views.
|
||||
*/
|
||||
@Nullable
|
||||
ScriptEngine getEngine();
|
||||
|
||||
/**
|
||||
* Return the engine name that will be used to instantiate the {@link ScriptEngine}.
|
||||
*/
|
||||
@Nullable
|
||||
String getEngineName();
|
||||
|
||||
/**
|
||||
* Return whether to use a shared engine for all threads or whether to create
|
||||
* thread-local engine instances for each thread.
|
||||
*/
|
||||
@Nullable
|
||||
Boolean isSharedEngine();
|
||||
|
||||
/**
|
||||
* Return the scripts to be loaded by the script engine (library or user provided).
|
||||
*/
|
||||
@Nullable
|
||||
String[] getScripts();
|
||||
|
||||
/**
|
||||
* Return the object where the render function belongs (optional).
|
||||
*/
|
||||
@Nullable
|
||||
String getRenderObject();
|
||||
|
||||
/**
|
||||
* Return the render function name (mandatory).
|
||||
*/
|
||||
@Nullable
|
||||
String getRenderFunction();
|
||||
|
||||
/**
|
||||
* Return the charset used to read script and template files.
|
||||
*/
|
||||
@Nullable
|
||||
Charset getCharset();
|
||||
|
||||
/**
|
||||
* Return the resource loader path(s) via a Spring resource location.
|
||||
*/
|
||||
@Nullable
|
||||
String getResourceLoaderPath();
|
||||
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
|
||||
protected ScriptEngine createEngineFromName() {
|
||||
if (this.scriptEngineManager == null) {
|
||||
this.scriptEngineManager = new ScriptEngineManager(getApplicationContext().getClassLoader());
|
||||
this.scriptEngineManager = new ScriptEngineManager(obtainApplicationContext().getClassLoader());
|
||||
}
|
||||
|
||||
ScriptEngine engine = StandardScriptUtils.retrieveEngineByName(this.scriptEngineManager, this.engineName);
|
||||
@@ -273,7 +273,7 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
protected ScriptTemplateConfig autodetectViewConfig() throws BeansException {
|
||||
try {
|
||||
return BeanFactoryUtils.beanOfTypeIncludingAncestors(
|
||||
getApplicationContext(), ScriptTemplateConfig.class, true, false);
|
||||
obtainApplicationContext(), ScriptTemplateConfig.class, true, false);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new ApplicationContextException("Expected a single ScriptTemplateConfig bean in the current " +
|
||||
@@ -284,18 +284,25 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
|
||||
@Override
|
||||
public boolean checkResourceExists(Locale locale) throws Exception {
|
||||
return (getResource(getUrl()) != null);
|
||||
String url = getUrl();
|
||||
Assert.state(url != null, "'url' not set");
|
||||
return (getResource(url) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Void> renderInternal(Map<String, Object> model, MediaType contentType, ServerWebExchange exchange) {
|
||||
protected Mono<Void> renderInternal(
|
||||
Map<String, Object> model, @Nullable MediaType contentType, ServerWebExchange exchange) {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
try {
|
||||
ScriptEngine engine = getEngine();
|
||||
Invocable invocable = (Invocable) engine;
|
||||
|
||||
String url = getUrl();
|
||||
Assert.state(url != null, "'url' not set");
|
||||
String template = getTemplate(url);
|
||||
|
||||
Function<String, String> templateLoader = path -> {
|
||||
try {
|
||||
return getTemplate(path);
|
||||
@@ -304,7 +311,9 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
};
|
||||
RenderingContext context = new RenderingContext(this.getApplicationContext(), this.locale, templateLoader, url);
|
||||
|
||||
RenderingContext context = new RenderingContext(
|
||||
obtainApplicationContext(), this.locale, templateLoader, url);
|
||||
|
||||
Object html;
|
||||
if (this.renderObject != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -151,7 +151,7 @@ public final class CloseStatus {
|
||||
* @param code the status code
|
||||
* @param reason the reason
|
||||
*/
|
||||
public CloseStatus(int code, String reason) {
|
||||
public CloseStatus(int code, @Nullable String reason) {
|
||||
Assert.isTrue((code >= 1000 && code < 5000), "Invalid status code");
|
||||
this.code = code;
|
||||
this.reason = reason;
|
||||
|
||||
@@ -51,7 +51,7 @@ public class HandshakeInfo {
|
||||
* @param principal the principal for the session
|
||||
* @param protocol the negotiated sub-protocol (may be {@code null})
|
||||
*/
|
||||
public HandshakeInfo(URI uri, HttpHeaders headers, Mono<Principal> principal, String protocol) {
|
||||
public HandshakeInfo(URI uri, HttpHeaders headers, Mono<Principal> principal, @Nullable String protocol) {
|
||||
Assert.notNull(uri, "URI is required");
|
||||
Assert.notNull(headers, "HttpHeaders are required");
|
||||
Assert.notNull(principal, "Principal is required");
|
||||
|
||||
@@ -29,6 +29,7 @@ import reactor.core.publisher.MonoProcessor;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.server.reactive.AbstractListenerReadPublisher;
|
||||
import org.springframework.http.server.reactive.AbstractListenerWriteProcessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
import org.springframework.web.reactive.socket.HandshakeInfo;
|
||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||
@@ -84,7 +85,7 @@ public abstract class AbstractListenerWebSocketSession<T> extends AbstractWebSoc
|
||||
* the session completion (success or error) (for client-side use).
|
||||
*/
|
||||
public AbstractListenerWebSocketSession(T delegate, String id, HandshakeInfo handshakeInfo,
|
||||
DataBufferFactory bufferFactory, MonoProcessor<Void> completionMono) {
|
||||
DataBufferFactory bufferFactory, @Nullable MonoProcessor<Void> completionMono) {
|
||||
|
||||
super(delegate, id, handshakeInfo, bufferFactory);
|
||||
this.completionMono = completionMono;
|
||||
|
||||
@@ -81,21 +81,17 @@ public abstract class AbstractWebSocketSession<T> implements WebSocketSession {
|
||||
return this.handshakeInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<WebSocketMessage> receive() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> send(Publisher<WebSocketMessage> messages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBufferFactory bufferFactory() {
|
||||
return this.bufferFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Flux<WebSocketMessage> receive();
|
||||
|
||||
@Override
|
||||
public abstract Mono<Void> send(Publisher<WebSocketMessage> messages);
|
||||
|
||||
|
||||
// WebSocketMessage factory methods
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoProcessor;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
@@ -52,7 +53,7 @@ public class JettyWebSocketSession extends AbstractListenerWebSocketSession<Sess
|
||||
}
|
||||
|
||||
public JettyWebSocketSession(Session session, HandshakeInfo info, DataBufferFactory factory,
|
||||
MonoProcessor<Void> completionMono) {
|
||||
@Nullable MonoProcessor<Void> completionMono) {
|
||||
|
||||
super(session, ObjectUtils.getIdentityHexString(session), info, factory, completionMono);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -29,6 +29,7 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoProcessor;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
import org.springframework.web.reactive.socket.HandshakeInfo;
|
||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||
@@ -44,13 +45,12 @@ import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
*/
|
||||
public class StandardWebSocketSession extends AbstractListenerWebSocketSession<Session> {
|
||||
|
||||
|
||||
public StandardWebSocketSession(Session session, HandshakeInfo info, DataBufferFactory factory) {
|
||||
this(session, info, factory, null);
|
||||
}
|
||||
|
||||
public StandardWebSocketSession(Session session, HandshakeInfo info, DataBufferFactory factory,
|
||||
MonoProcessor<Void> completionMono) {
|
||||
@Nullable MonoProcessor<Void> completionMono) {
|
||||
|
||||
super(session, session.getId(), info, factory, completionMono);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -28,6 +28,7 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoProcessor;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
import org.springframework.web.reactive.socket.HandshakeInfo;
|
||||
@@ -44,13 +45,12 @@ import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
*/
|
||||
public class UndertowWebSocketSession extends AbstractListenerWebSocketSession<WebSocketChannel> {
|
||||
|
||||
|
||||
public UndertowWebSocketSession(WebSocketChannel channel, HandshakeInfo info, DataBufferFactory factory) {
|
||||
this(channel, info, factory, null);
|
||||
}
|
||||
|
||||
public UndertowWebSocketSession(WebSocketChannel channel, HandshakeInfo info,
|
||||
DataBufferFactory factory, MonoProcessor<Void> completionMono) {
|
||||
DataBufferFactory factory, @Nullable MonoProcessor<Void> completionMono) {
|
||||
|
||||
super(channel, ObjectUtils.getIdentityHexString(channel), info, factory, completionMono);
|
||||
}
|
||||
|
||||
@@ -171,7 +171,6 @@ public class HandshakeWebSocketService implements WebSocketService, Lifecycle {
|
||||
|
||||
@Override
|
||||
public Mono<Void> handleRequest(ServerWebExchange exchange, WebSocketHandler handler) {
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
HttpMethod method = request.getMethod();
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
@@ -181,7 +180,8 @@ public class HandshakeWebSocketService implements WebSocketService, Lifecycle {
|
||||
}
|
||||
|
||||
if (HttpMethod.GET != method) {
|
||||
return Mono.error(new MethodNotAllowedException(method, Collections.singleton(HttpMethod.GET)));
|
||||
return Mono.error(new MethodNotAllowedException(
|
||||
request.getMethodValue(), Collections.singleton(HttpMethod.GET)));
|
||||
}
|
||||
|
||||
if (!"WebSocket".equalsIgnoreCase(headers.getUpgrade())) {
|
||||
|
||||
@@ -152,7 +152,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
||||
return ((ServletServerHttpResponse) response).getServletResponse();
|
||||
}
|
||||
|
||||
private HandshakeInfo getHandshakeInfo(ServerWebExchange exchange, String protocol) {
|
||||
private HandshakeInfo getHandshakeInfo(ServerWebExchange exchange, @Nullable String protocol) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
Mono<Principal> principal = exchange.getPrincipal();
|
||||
return new HandshakeInfo(request.getURI(), request.getHeaders(), principal, protocol);
|
||||
@@ -177,7 +177,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
||||
|
||||
private final String protocol;
|
||||
|
||||
public WebSocketHandlerContainer(JettyWebSocketHandlerAdapter adapter, String protocol) {
|
||||
public WebSocketHandlerContainer(JettyWebSocketHandlerAdapter adapter, @Nullable String protocol) {
|
||||
this.adapter = adapter;
|
||||
this.protocol = protocol;
|
||||
}
|
||||
@@ -186,6 +186,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life
|
||||
return this.adapter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getProtocol() {
|
||||
return this.protocol;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class ReactorNettyRequestUpgradeStrategy implements RequestUpgradeStrateg
|
||||
(in, out) -> handler.handle(new ReactorNettyWebSocketSession(in, out, info, bufferFactory)));
|
||||
}
|
||||
|
||||
private HandshakeInfo getHandshakeInfo(ServerWebExchange exchange, String protocol) {
|
||||
private HandshakeInfo getHandshakeInfo(ServerWebExchange exchange, @Nullable String protocol) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
Mono<Principal> principal = exchange.getPrincipal();
|
||||
return new HandshakeInfo(request.getURI(), request.getHeaders(), principal, protocol);
|
||||
|
||||
@@ -92,7 +92,7 @@ public class TomcatRequestUpgradeStrategy implements RequestUpgradeStrategy {
|
||||
return ((ServletServerHttpResponse) response).getServletResponse();
|
||||
}
|
||||
|
||||
private HandshakeInfo getHandshakeInfo(ServerWebExchange exchange, String protocol) {
|
||||
private HandshakeInfo getHandshakeInfo(ServerWebExchange exchange, @Nullable String protocol) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
Mono<Principal> principal = exchange.getPrincipal();
|
||||
return new HandshakeInfo(request.getURI(), request.getHeaders(), principal, protocol);
|
||||
|
||||
@@ -42,8 +42,8 @@ import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
@@ -52,16 +52,15 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.springframework.core.io.buffer.support.DataBufferTestUtils.dumpString;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
|
||||
import static org.springframework.web.method.ResolvableMethod.on;
|
||||
import static org.springframework.web.reactive.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.io.buffer.support.DataBufferTestUtils.*;
|
||||
import static org.springframework.http.MediaType.*;
|
||||
import static org.springframework.web.method.ResolvableMethod.*;
|
||||
import static org.springframework.web.reactive.HandlerMapping.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractMessageWriterResultHandler}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class MessageWriterResultHandlerTests {
|
||||
|
||||
Reference in New Issue
Block a user