Sync argument resolution and HandlerMethod invocation
HandlerMethodArgumentResolver is a non-blocking contract, however only implementations that read the request body require blocking. This commit introduces SyncMethodArgumentResolver as an extension of the non-blocking contract that allows synchronous implementations to use synchronous argument resolution. There is also a SyncInvocableHandlerMethod extension that uses only sync argument resolvers and allows a synchronous invocation. Issue: SPR-14543
This commit is contained in:
@@ -87,7 +87,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
* @return Publisher that produces a single HandlerResult or an error signal;
|
||||
* never throws an exception
|
||||
*/
|
||||
public Mono<HandlerResult> invokeForRequest(ServerWebExchange exchange,
|
||||
public Mono<HandlerResult> invoke(ServerWebExchange exchange,
|
||||
BindingContext bindingContext, Object... providedArgs) {
|
||||
|
||||
return resolveArguments(exchange, bindingContext, providedArgs).then(args -> {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.method;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
|
||||
/**
|
||||
* An extension of {@link HandlerMethodArgumentResolver} for implementations
|
||||
* that are synchronous in nature and do not block to resolve values.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface SyncHandlerMethodArgumentResolver extends HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
default Mono<Object> resolveArgument(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
Optional<Object> value = resolveArgumentValue(parameter, context, exchange);
|
||||
return Mono.justOrEmpty(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the method argument value synchronously returning an optional value.
|
||||
* @param parameter the method parameter
|
||||
* @param bindingContext the binding context to use
|
||||
* @param exchange the current exchange
|
||||
* @return the resolved value if any
|
||||
*/
|
||||
Optional<Object> resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* An extension of {@code InvocableHandlerMethod} for use with
|
||||
* {@link SyncHandlerMethodArgumentResolver}s which in turn enables synchronous
|
||||
* handler method invocation via {@link #invokeForHandlerResult}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class SyncInvocableHandlerMethod extends InvocableHandlerMethod {
|
||||
|
||||
|
||||
public SyncInvocableHandlerMethod(HandlerMethod handlerMethod) {
|
||||
super(handlerMethod);
|
||||
}
|
||||
|
||||
public SyncInvocableHandlerMethod(Object bean, Method method) {
|
||||
super(bean, method);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overloaded variant of the same setter from the base class that ensures
|
||||
* all resolvers are {@link SyncHandlerMethodArgumentResolver}.
|
||||
*/
|
||||
@Override
|
||||
public void setHandlerMethodArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
resolvers.forEach(resolver ->
|
||||
Assert.isInstanceOf(SyncHandlerMethodArgumentResolver.class, resolver,
|
||||
"Expected sync argument resolver: " + resolver.getClass().getName()));
|
||||
super.setHandlerMethodArgumentResolvers(resolvers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate to the base class {@link #invoke} and also wait for the result.
|
||||
* Since all argument resolvers are synchronous this won't actually block.
|
||||
*/
|
||||
public HandlerResult invokeForHandlerResult(ServerWebExchange exchange,
|
||||
BindingContext bindingContext, Object... providedArgs) {
|
||||
|
||||
// This will not block
|
||||
return super.invoke(exchange, bindingContext, providedArgs).block();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
public abstract class AbstractNamedValueArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final ConfigurableBeanFactory configurableBeanFactory;
|
||||
|
||||
@@ -67,7 +67,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
* and #{...} SpEL expressions in default values, or {@code null} if default
|
||||
* values are not expected to contain expressions
|
||||
*/
|
||||
public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
public AbstractNamedValueArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
this.configurableBeanFactory = beanFactory;
|
||||
this.expressionContext = (beanFactory != null ? new BeanExpressionContext(beanFactory, null) : null);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* An extension of {@link AbstractNamedValueArgumentResolver} for named value
|
||||
* resolvers that are synchronous and yet non-blocking. Sub-classes implement
|
||||
* the synchronous {@link #resolveNamedValue} to which the asynchronous
|
||||
* {@link #resolveName} delegates to by default.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class AbstractNamedValueSyncArgumentResolver extends AbstractNamedValueArgumentResolver
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
public AbstractNamedValueSyncArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter parameter,
|
||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
// This will not block
|
||||
Object value = resolveArgument(parameter, bindingContext, exchange).block();
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
return Mono.justOrEmpty(resolveNamedValue(name, parameter, exchange));
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract method for synchronous resolution of method argument values
|
||||
* that sub-classes must implement.
|
||||
* @param name the name of the value being resolved
|
||||
* @param parameter the method parameter to resolve to an argument value
|
||||
* (pre-nested in case of a {@link java.util.Optional} declaration)
|
||||
* @param exchange the current exchange
|
||||
* @return the resolved argument value, if any
|
||||
*/
|
||||
protected abstract Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange);
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -35,7 +35,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
public class CookieValueMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
@@ -60,16 +60,19 @@ public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodA
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
HttpCookie cookie = exchange.getRequest().getCookies().getFirst(name);
|
||||
if (HttpCookie.class.isAssignableFrom(parameter.getNestedParameterType())) {
|
||||
return Mono.justOrEmpty(cookie);
|
||||
Class<?> paramType = parameter.getNestedParameterType();
|
||||
if (HttpCookie.class.isAssignableFrom(paramType)) {
|
||||
return Optional.ofNullable(cookie);
|
||||
}
|
||||
else if (cookie != null) {
|
||||
return Mono.justOrEmpty(cookie.getValue());
|
||||
return Optional.ofNullable(cookie.getValue());
|
||||
}
|
||||
else {
|
||||
return Mono.empty();
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
@@ -33,7 +33,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
@@ -58,9 +58,11 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMet
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
// No name to resolve
|
||||
return Mono.empty();
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -29,7 +29,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ModelArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
public class ModelArgumentResolver implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
@Override
|
||||
@@ -38,10 +38,10 @@ public class ModelArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
return Mono.just(bindingContext.getModel());
|
||||
return Optional.of(bindingContext.getModel());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,14 +20,12 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -40,7 +38,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @since 5.0
|
||||
* @see PathVariableMethodArgumentResolver
|
||||
*/
|
||||
public class PathVariableMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
public class PathVariableMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
@Override
|
||||
@@ -54,12 +52,12 @@ public class PathVariableMapMethodArgumentResolver implements HandlerMethodArgum
|
||||
* Return a Map with all URI template variables or an empty map.
|
||||
*/
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
Optional<Object> value = exchange.getAttribute(name);
|
||||
return (value.isPresent() ? Mono.just(value.get()) : Mono.just(Collections.emptyMap()));
|
||||
Object value = exchange.getAttribute(name).orElse(Collections.emptyMap());
|
||||
return Optional.of(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ package org.springframework.web.reactive.result.method.annotation;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -51,7 +49,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @since 5.0
|
||||
* @see PathVariableMapMethodArgumentResolver
|
||||
*/
|
||||
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
public class PathVariableMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
|
||||
public PathVariableMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
@@ -79,14 +77,12 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
String attributeName = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
Optional<Object> optional = exchange.getAttribute(attributeName);
|
||||
Object value = null;
|
||||
if (optional.isPresent()) {
|
||||
value = ((Map<String, String>) optional.get()).get(name);
|
||||
}
|
||||
return Mono.justOrEmpty(value);
|
||||
return exchange.getAttribute(attributeName)
|
||||
.map(value -> ((Map<String, String>) value).get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -31,7 +31,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @since 5.0
|
||||
* @see SessionAttributeMethodArgumentResolver
|
||||
*/
|
||||
public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
|
||||
public RequestAttributeMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
@@ -52,8 +52,10 @@ public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueMe
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange){
|
||||
return Mono.justOrEmpty(exchange.getAttribute(name));
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
return exchange.getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,15 +17,14 @@
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -41,7 +40,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @since 5.0
|
||||
* @see RequestHeaderMethodArgumentResolver
|
||||
*/
|
||||
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
public class RequestHeaderMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
@@ -50,16 +49,17 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
HttpHeaders headers = exchange.getRequest().getHeaders();
|
||||
if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
|
||||
return Mono.just(headers);
|
||||
}
|
||||
else {
|
||||
return Mono.just(headers.toSingleValueMap());
|
||||
}
|
||||
Object value = (isMultiValueMap(parameter) ? headers : headers.toSingleValueMap());
|
||||
return Optional.of(value);
|
||||
}
|
||||
|
||||
private boolean isMultiValueMap(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
return MultiValueMap.class.isAssignableFrom(paramType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@ package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -44,8 +43,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @since 5.0
|
||||
* @see RequestHeaderMapMethodArgumentResolver
|
||||
*/
|
||||
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...}
|
||||
@@ -70,13 +68,15 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
List<String> headerValues = exchange.getRequest().getHeaders().get(name);
|
||||
Object result = null;
|
||||
if (headerValues != null) {
|
||||
result = (headerValues.size() == 1 ? headerValues.get(0) : headerValues);
|
||||
}
|
||||
return Mono.justOrEmpty(result);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -192,6 +192,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||
resolvers.add(new HttpEntityArgumentResolver(getMessageReaders(), adapterRegistry));
|
||||
resolvers.add(new ModelArgumentResolver());
|
||||
resolvers.add(new ServerWebExchangeArgumentResolver());
|
||||
resolvers.add(new WebSessionArgumentResolver());
|
||||
|
||||
// Custom resolvers
|
||||
if (getCustomArgumentResolvers() != null) {
|
||||
@@ -214,7 +215,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||
InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod);
|
||||
invocable.setHandlerMethodArgumentResolvers(getArgumentResolvers());
|
||||
BindingContext bindingContext = new BindingContext(getWebBindingInitializer());
|
||||
return invocable.invokeForRequest(exchange, bindingContext)
|
||||
return invocable.invoke(exchange, bindingContext)
|
||||
.map(result -> result.setExceptionHandler(
|
||||
ex -> handleException(ex, handlerMethod, bindingContext, exchange)))
|
||||
.otherwise(ex -> handleException(
|
||||
@@ -232,7 +233,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||
}
|
||||
invocable.setHandlerMethodArgumentResolvers(getArgumentResolvers());
|
||||
bindingContext.getModel().clear();
|
||||
return invocable.invokeForRequest(exchange, bindingContext, ex);
|
||||
return invocable.invoke(exchange, bindingContext, ex);
|
||||
}
|
||||
catch (Throwable invocationEx) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
|
||||
@@ -17,15 +17,14 @@
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -43,7 +42,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @since 5.0
|
||||
* @see RequestParamMethodArgumentResolver
|
||||
*/
|
||||
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
public class RequestParamMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
@@ -57,16 +56,17 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
|
||||
if (MultiValueMap.class.isAssignableFrom(paramType)) {
|
||||
return Mono.just(queryParams);
|
||||
}
|
||||
else {
|
||||
return Mono.just(queryParams.toSingleValueMap());
|
||||
}
|
||||
Object value = (isMultiValueMap(parameter) ? queryParams : queryParams.toSingleValueMap());
|
||||
return Optional.of(value);
|
||||
}
|
||||
|
||||
private boolean isMultiValueMap(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
return MultiValueMap.class.isAssignableFrom(paramType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -50,7 +51,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @since 5.0
|
||||
* @see RequestParamMapMethodArgumentResolver
|
||||
*/
|
||||
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
private final boolean useDefaultResolution;
|
||||
|
||||
@@ -91,13 +92,15 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
List<String> paramValues = exchange.getRequest().getQueryParams().get(name);
|
||||
Object result = null;
|
||||
if (paramValues != null) {
|
||||
result = (paramValues.size() == 1 ? paramValues.get(0) : paramValues);
|
||||
}
|
||||
return Mono.justOrEmpty(result);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,31 +15,32 @@
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebSession;
|
||||
|
||||
/**
|
||||
* Resolves ServerWebExchange-related method argument values of the following types:
|
||||
* <ul>
|
||||
* <li>{@link ServerWebExchange}
|
||||
* <li>{@link ServerHttpRequest}
|
||||
* <li>{@link WebSession}
|
||||
* <li>{@link HttpMethod}
|
||||
* <li>{@link ServerHttpResponse}
|
||||
* </ul>
|
||||
*
|
||||
* <p>For the {@code WebSession} see {@link WebSessionArgumentResolver}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
* @see WebSessionArgumentResolver
|
||||
*/
|
||||
public class ServerWebExchangeArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
public class ServerWebExchangeArgumentResolver implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
@@ -47,35 +48,33 @@ public class ServerWebExchangeArgumentResolver implements HandlerMethodArgumentR
|
||||
return (ServerWebExchange.class.isAssignableFrom(paramType) ||
|
||||
ServerHttpRequest.class.isAssignableFrom(paramType) ||
|
||||
ServerHttpResponse.class.isAssignableFrom(paramType) ||
|
||||
WebSession.class.isAssignableFrom(paramType) ||
|
||||
HttpMethod.class == paramType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
Object value;
|
||||
if (ServerWebExchange.class.isAssignableFrom(paramType)) {
|
||||
return Mono.just(exchange);
|
||||
value = exchange;
|
||||
}
|
||||
else if (ServerHttpRequest.class.isAssignableFrom(paramType)) {
|
||||
return Mono.just(exchange.getRequest());
|
||||
value = exchange.getRequest();
|
||||
}
|
||||
else if (ServerHttpResponse.class.isAssignableFrom(paramType)) {
|
||||
return Mono.just(exchange.getResponse());
|
||||
}
|
||||
else if (WebSession.class.isAssignableFrom(paramType)) {
|
||||
return exchange.getSession().cast(Object.class);
|
||||
value = exchange.getResponse();
|
||||
}
|
||||
else if (HttpMethod.class == paramType) {
|
||||
return Mono.just(exchange.getRequest().getMethod());
|
||||
value = exchange.getRequest().getMethod();
|
||||
}
|
||||
else {
|
||||
// should never happen...
|
||||
return Mono.error(new UnsupportedOperationException(
|
||||
"Unknown parameter type: " + paramType + " in method: " + parameter.getMethod()));
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown parameter type: " + paramType + " in method: " + parameter.getMethod());
|
||||
}
|
||||
return Optional.of(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @since 5.0
|
||||
* @see RequestAttributeMethodArgumentResolver
|
||||
*/
|
||||
public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueArgumentResolver {
|
||||
|
||||
|
||||
public SessionAttributeMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
@@ -53,9 +53,13 @@ public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueMe
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange){
|
||||
return exchange.getSession().map(session -> session.getAttribute(name))
|
||||
.filter(Optional::isPresent).map(Optional::get);
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
return exchange.getSession()
|
||||
.map(session -> session.getAttribute(name))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebSession;
|
||||
|
||||
/**
|
||||
* Resolves method argument value of type {@link WebSession}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
* @see ServerWebExchangeArgumentResolver
|
||||
*/
|
||||
public class WebSessionArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return (WebSession.class.isAssignableFrom(parameter.getParameterType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
if (WebSession.class.isAssignableFrom(paramType)) {
|
||||
return exchange.getSession().cast(Object.class);
|
||||
}
|
||||
else {
|
||||
// should never happen...
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown parameter type: " + paramType + " in method: " + parameter.getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public class InvocableHandlerMethodTests {
|
||||
@Test
|
||||
public void invokeMethodWithNoArguments() throws Exception {
|
||||
InvocableHandlerMethod hm = handlerMethod("noArgs");
|
||||
Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, new BindingContext());
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
assertHandlerResultValue(mono, "success");
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class InvocableHandlerMethodTests {
|
||||
public void invokeMethodWithNoValue() throws Exception {
|
||||
InvocableHandlerMethod hm = handlerMethod("singleArg");
|
||||
addResolver(hm, Mono.empty());
|
||||
Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, new BindingContext());
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
assertHandlerResultValue(mono, "success:null");
|
||||
}
|
||||
@@ -82,7 +82,7 @@ public class InvocableHandlerMethodTests {
|
||||
public void invokeMethodWithValue() throws Exception {
|
||||
InvocableHandlerMethod hm = handlerMethod("singleArg");
|
||||
addResolver(hm, Mono.just("value1"));
|
||||
Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, new BindingContext());
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
assertHandlerResultValue(mono, "success:value1");
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class InvocableHandlerMethodTests {
|
||||
@Test
|
||||
public void noMatchingResolver() throws Exception {
|
||||
InvocableHandlerMethod hm = handlerMethod("singleArg");
|
||||
Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, new BindingContext());
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
ScriptedSubscriber.create().expectNextCount(0)
|
||||
.consumeErrorWith(error -> {
|
||||
@@ -105,7 +105,7 @@ public class InvocableHandlerMethodTests {
|
||||
public void resolverThrowsException() throws Exception {
|
||||
InvocableHandlerMethod hm = handlerMethod("singleArg");
|
||||
addResolver(hm, Mono.error(new UnsupportedMediaTypeStatusException("boo")));
|
||||
Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, new BindingContext());
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
ScriptedSubscriber.create().expectNextCount(0)
|
||||
.consumeErrorWith(error -> {
|
||||
@@ -119,7 +119,7 @@ public class InvocableHandlerMethodTests {
|
||||
public void illegalArgumentExceptionIsWrappedWithInvocationDetails() throws Exception {
|
||||
InvocableHandlerMethod hm = handlerMethod("singleArg");
|
||||
addResolver(hm, Mono.just(1));
|
||||
Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, new BindingContext());
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
ScriptedSubscriber.create().expectNextCount(0)
|
||||
.consumeErrorWith(error -> {
|
||||
@@ -134,7 +134,7 @@ public class InvocableHandlerMethodTests {
|
||||
@Test
|
||||
public void invocationTargetExceptionIsUnwrapped() throws Exception {
|
||||
InvocableHandlerMethod hm = handlerMethod("exceptionMethod");
|
||||
Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, new BindingContext());
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
ScriptedSubscriber.create().expectNextCount(0)
|
||||
.consumeErrorWith(error -> {
|
||||
|
||||
@@ -380,7 +380,7 @@ public class RequestMappingInfoHandlerMappingTests {
|
||||
|
||||
BindingContext bindingContext = new BindingContext();
|
||||
InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod);
|
||||
Mono<HandlerResult> mono = invocable.invokeForRequest(exchange, bindingContext);
|
||||
Mono<HandlerResult> mono = invocable.invoke(exchange, bindingContext);
|
||||
|
||||
HandlerResult result = mono.block();
|
||||
assertNotNull(result);
|
||||
|
||||
@@ -69,7 +69,6 @@ public class ServerWebExchangeArgumentResolverTests {
|
||||
assertTrue(this.resolver.supportsParameter(parameter(ServerWebExchange.class)));
|
||||
assertTrue(this.resolver.supportsParameter(parameter(ServerHttpRequest.class)));
|
||||
assertTrue(this.resolver.supportsParameter(parameter(ServerHttpResponse.class)));
|
||||
assertTrue(this.resolver.supportsParameter(parameter(WebSession.class)));
|
||||
assertTrue(this.resolver.supportsParameter(parameter(HttpMethod.class)));
|
||||
assertFalse(this.resolver.supportsParameter(parameter(String.class)));
|
||||
}
|
||||
@@ -79,7 +78,6 @@ public class ServerWebExchangeArgumentResolverTests {
|
||||
testResolveArgument(parameter(ServerWebExchange.class), this.exchange);
|
||||
testResolveArgument(parameter(ServerHttpRequest.class), this.exchange.getRequest());
|
||||
testResolveArgument(parameter(ServerHttpResponse.class), this.exchange.getResponse());
|
||||
testResolveArgument(parameter(WebSession.class), this.exchange.getSession().block());
|
||||
testResolveArgument(parameter(HttpMethod.class), HttpMethod.GET);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user