Polish method parameter handling
This commit is contained in:
@@ -25,10 +25,12 @@ import java.util.Set;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.ReactiveAdapter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.web.reactive.HandlerMapping;
|
import org.springframework.web.reactive.HandlerMapping;
|
||||||
|
import org.springframework.web.reactive.HandlerResult;
|
||||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
@@ -72,6 +74,14 @@ public abstract class AbstractHandlerResultHandler implements Ordered {
|
|||||||
return this.adapterRegistry;
|
return this.adapterRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to get a ReactiveAdapter for the top-level return value type.
|
||||||
|
*/
|
||||||
|
protected ReactiveAdapter getAdapter(HandlerResult result) {
|
||||||
|
Class<?> returnType = result.getReturnType().getRawClass();
|
||||||
|
return getAdapterRegistry().getAdapter(returnType, result.getReturnValue());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the configured {@link RequestedContentTypeResolver}.
|
* Return the configured {@link RequestedContentTypeResolver}.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -113,11 +113,7 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||||||
|
|
||||||
ResolvableType bodyType = ResolvableType.forMethodParameter(bodyParameter);
|
ResolvableType bodyType = ResolvableType.forMethodParameter(bodyParameter);
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(bodyType.resolve());
|
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(bodyType.resolve());
|
||||||
|
ResolvableType elementType = (adapter != null ? bodyType.getGeneric(0) : bodyType);
|
||||||
ResolvableType elementType = ResolvableType.forMethodParameter(bodyParameter);
|
|
||||||
if (adapter != null) {
|
|
||||||
elementType = elementType.getGeneric(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerHttpRequest request = exchange.getRequest();
|
ServerHttpRequest request = exchange.getRequest();
|
||||||
ServerHttpResponse response = exchange.getResponse();
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
@@ -139,8 +135,8 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||||||
else {
|
else {
|
||||||
flux = reader.read(elementType, request, readHints);
|
flux = reader.read(elementType, request, readHints);
|
||||||
}
|
}
|
||||||
flux = flux.onErrorResumeWith(ex -> Flux.error(wrapReadError(ex, bodyParameter)));
|
flux = flux.onErrorResumeWith(ex -> Flux.error(getReadError(bodyParameter, ex)));
|
||||||
if (checkRequired(adapter, isBodyRequired)) {
|
if (isBodyRequired || !adapter.supportsEmpty()) {
|
||||||
flux = flux.switchIfEmpty(Flux.error(getRequiredBodyError(bodyParameter)));
|
flux = flux.switchIfEmpty(Flux.error(getRequiredBodyError(bodyParameter)));
|
||||||
}
|
}
|
||||||
Object[] hints = extractValidationHints(bodyParameter);
|
Object[] hints = extractValidationHints(bodyParameter);
|
||||||
@@ -159,8 +155,8 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||||||
else {
|
else {
|
||||||
mono = reader.readMono(elementType, request, readHints);
|
mono = reader.readMono(elementType, request, readHints);
|
||||||
}
|
}
|
||||||
mono = mono.otherwise(ex -> Mono.error(wrapReadError(ex, bodyParameter)));
|
mono = mono.otherwise(ex -> Mono.error(getReadError(bodyParameter, ex)));
|
||||||
if (checkRequired(adapter, isBodyRequired)) {
|
if (isBodyRequired || (adapter != null && !adapter.supportsEmpty())) {
|
||||||
mono = mono.otherwiseIfEmpty(Mono.error(getRequiredBodyError(bodyParameter)));
|
mono = mono.otherwiseIfEmpty(Mono.error(getRequiredBodyError(bodyParameter)));
|
||||||
}
|
}
|
||||||
Object[] hints = extractValidationHints(bodyParameter);
|
Object[] hints = extractValidationHints(bodyParameter);
|
||||||
@@ -181,15 +177,11 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||||||
return Mono.error(new UnsupportedMediaTypeStatusException(mediaType, this.supportedMediaTypes));
|
return Mono.error(new UnsupportedMediaTypeStatusException(mediaType, this.supportedMediaTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ServerWebInputException wrapReadError(Throwable ex, MethodParameter parameter) {
|
private ServerWebInputException getReadError(MethodParameter parameter, Throwable ex) {
|
||||||
return new ServerWebInputException("Failed to read HTTP message", parameter, ex);
|
return new ServerWebInputException("Failed to read HTTP message", parameter, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean checkRequired(ReactiveAdapter adapter, boolean isBodyRequired) {
|
private ServerWebInputException getRequiredBodyError(MethodParameter parameter) {
|
||||||
return adapter != null && !adapter.supportsEmpty() || isBodyRequired;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ServerWebInputException getRequiredBodyError(MethodParameter parameter) {
|
|
||||||
return new ServerWebInputException("Required request body is missing: " +
|
return new ServerWebInputException("Required request body is missing: " +
|
||||||
parameter.getMethod().toGenericString());
|
parameter.getMethod().toGenericString());
|
||||||
}
|
}
|
||||||
@@ -199,7 +191,7 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||||||
* a (possibly empty) Object[] with validation hints. A return value of
|
* a (possibly empty) Object[] with validation hints. A return value of
|
||||||
* {@code null} indicates that validation is not required.
|
* {@code null} indicates that validation is not required.
|
||||||
*/
|
*/
|
||||||
protected Object[] extractValidationHints(MethodParameter parameter) {
|
private Object[] extractValidationHints(MethodParameter parameter) {
|
||||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||||
for (Annotation ann : annotations) {
|
for (Annotation ann : annotations) {
|
||||||
Validated validAnnot = AnnotationUtils.getAnnotation(ann, Validated.class);
|
Validated validAnnot = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||||
@@ -211,8 +203,8 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void validate(Object target, Object[] validationHints,
|
private void validate(Object target, Object[] validationHints, MethodParameter param,
|
||||||
MethodParameter param, BindingContext binding, ServerWebExchange exchange) {
|
BindingContext binding, ServerWebExchange exchange) {
|
||||||
|
|
||||||
String name = Conventions.getVariableNameForParameter(param);
|
String name = Conventions.getVariableNameForParameter(param);
|
||||||
WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name);
|
WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name);
|
||||||
|
|||||||
@@ -93,21 +93,19 @@ public abstract class AbstractMessageWriterResultHandler extends AbstractHandler
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected Mono<Void> writeBody(Object body, MethodParameter bodyParameter, ServerWebExchange exchange) {
|
protected Mono<Void> writeBody(Object body, MethodParameter bodyParameter, ServerWebExchange exchange) {
|
||||||
|
|
||||||
ResolvableType valueType = ResolvableType.forMethodParameter(bodyParameter);
|
ResolvableType bodyType = ResolvableType.forMethodParameter(bodyParameter);
|
||||||
Class<?> valueClass = valueType.resolve();
|
Class<?> bodyClass = bodyType.resolve();
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(valueClass, body);
|
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(bodyClass, body);
|
||||||
|
|
||||||
Publisher<?> publisher;
|
Publisher<?> publisher;
|
||||||
ResolvableType elementType;
|
ResolvableType elementType;
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
publisher = adapter.toPublisher(body);
|
publisher = adapter.toPublisher(body);
|
||||||
elementType = adapter.isNoValue() ?
|
elementType = adapter.isNoValue() ? ResolvableType.forClass(Void.class) : bodyType.getGeneric(0);
|
||||||
ResolvableType.forClass(Void.class) : valueType.getGeneric(0);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
publisher = Mono.justOrEmpty(body);
|
publisher = Mono.justOrEmpty(body);
|
||||||
elementType = (valueClass == null && body != null ?
|
elementType = (bodyClass == null && body != null ? ResolvableType.forInstance(body) : bodyType);
|
||||||
ResolvableType.forInstance(body) : valueType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (void.class == elementType.getRawClass() || Void.class == elementType.getRawClass()) {
|
if (void.class == elementType.getRawClass() || Void.class == elementType.getRawClass()) {
|
||||||
@@ -122,7 +120,7 @@ public abstract class AbstractMessageWriterResultHandler extends AbstractHandler
|
|||||||
if (messageWriter.canWrite(elementType, bestMediaType)) {
|
if (messageWriter.canWrite(elementType, bestMediaType)) {
|
||||||
return (messageWriter instanceof ServerHttpMessageWriter ?
|
return (messageWriter instanceof ServerHttpMessageWriter ?
|
||||||
((ServerHttpMessageWriter<?>) messageWriter).write((Publisher) publisher,
|
((ServerHttpMessageWriter<?>) messageWriter).write((Publisher) publisher,
|
||||||
valueType, elementType, bestMediaType, request, response, Collections.emptyMap()) :
|
bodyType, elementType, bestMediaType, request, response, Collections.emptyMap()) :
|
||||||
messageWriter.write((Publisher) publisher, elementType,
|
messageWriter.write((Publisher) publisher, elementType,
|
||||||
bestMediaType, response, Collections.emptyMap()));
|
bestMediaType, response, Collections.emptyMap()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ import reactor.core.publisher.Mono;
|
|||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
import org.springframework.core.ResolvableType;
|
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.RequestEntity;
|
import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.codec.HttpMessageReader;
|
import org.springframework.http.codec.HttpMessageReader;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
@@ -69,29 +67,20 @@ public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Object> resolveArgument(MethodParameter param, BindingContext bindingContext,
|
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||||
ServerWebExchange exchange) {
|
ServerWebExchange exchange) {
|
||||||
|
|
||||||
ResolvableType entityType = ResolvableType.forMethodParameter(param);
|
Class<?> entityType = parameter.getParameterType();
|
||||||
MethodParameter bodyParameter = new MethodParameter(param);
|
|
||||||
bodyParameter.increaseNestingLevel();
|
|
||||||
|
|
||||||
return readBody(bodyParameter, false, bindingContext, exchange)
|
return readBody(parameter.nested(), false, bindingContext, exchange)
|
||||||
.map(body -> createHttpEntity(body, entityType, exchange))
|
.map(body -> createEntity(body, entityType, exchange.getRequest()))
|
||||||
.defaultIfEmpty(createHttpEntity(null, entityType, exchange));
|
.defaultIfEmpty(createEntity(null, entityType, exchange.getRequest()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object createHttpEntity(Object body, ResolvableType entityType,
|
private Object createEntity(Object body, Class<?> entityType, ServerHttpRequest request) {
|
||||||
ServerWebExchange exchange) {
|
return RequestEntity.class.equals(entityType) ?
|
||||||
|
new RequestEntity<>(body, request.getHeaders(), request.getMethod(), request.getURI()) :
|
||||||
ServerHttpRequest request = exchange.getRequest();
|
new HttpEntity<>(body, request.getHeaders());
|
||||||
HttpHeaders headers = request.getHeaders();
|
|
||||||
if (RequestEntity.class == entityType.getRawClass()) {
|
|
||||||
return new RequestEntity<>(body, headers, request.getMethod(), request.getURI());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new HttpEntity<>(body, headers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.springframework.core.ReactiveAdapter;
|
|||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@@ -122,9 +123,10 @@ public class ModelAttributeMethodArgumentResolver implements HandlerMethodArgume
|
|||||||
|
|
||||||
ResolvableType type = ResolvableType.forMethodParameter(parameter);
|
ResolvableType type = ResolvableType.forMethodParameter(parameter);
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type.resolve());
|
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type.resolve());
|
||||||
Class<?> valueType = (adapter != null ? type.resolveGeneric(0) : parameter.getParameterType());
|
ResolvableType valueType = (adapter != null ? type.getGeneric(0) : type);
|
||||||
|
|
||||||
String name = getAttributeName(valueType, parameter);
|
String name = getAttributeName(valueType, parameter);
|
||||||
Mono<?> valueMono = getAttributeMono(name, valueType, parameter, context, exchange);
|
Mono<?> valueMono = getAttributeMono(name, valueType, context.getModel());
|
||||||
|
|
||||||
Map<String, Object> model = context.getModel().asMap();
|
Map<String, Object> model = context.getModel().asMap();
|
||||||
MonoProcessor<BindingResult> bindingResultMono = MonoProcessor.create();
|
MonoProcessor<BindingResult> bindingResultMono = MonoProcessor.create();
|
||||||
@@ -146,10 +148,10 @@ public class ModelAttributeMethodArgumentResolver implements HandlerMethodArgume
|
|||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
return adapter.fromPublisher(errors.hasErrors() ?
|
return adapter.fromPublisher(errors.hasErrors() ?
|
||||||
Mono.error(new WebExchangeBindException(parameter, errors)) :
|
Mono.error(new WebExchangeBindException(parameter, errors)) :
|
||||||
Mono.just(value));
|
valueMono);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (errors.hasErrors() && checkErrorsArgument(parameter)) {
|
if (errors.hasErrors() && !hasErrorsArgument(parameter)) {
|
||||||
throw new WebExchangeBindException(parameter, errors);
|
throw new WebExchangeBindException(parameter, errors);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@@ -158,46 +160,37 @@ public class ModelAttributeMethodArgumentResolver implements HandlerMethodArgume
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAttributeName(Class<?> valueType, MethodParameter parameter) {
|
private String getAttributeName(ResolvableType valueType, MethodParameter parameter) {
|
||||||
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
|
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||||
if (annot != null && StringUtils.hasText(annot.value())) {
|
if (annot != null && StringUtils.hasText(annot.value())) {
|
||||||
return annot.value();
|
return annot.value();
|
||||||
}
|
}
|
||||||
// TODO: Conventions does not deal with async wrappers
|
// TODO: Conventions does not deal with async wrappers
|
||||||
return ClassUtils.getShortNameAsProperty(valueType);
|
return ClassUtils.getShortNameAsProperty(valueType.getRawClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<?> getAttributeMono(String attributeName, Class<?> attributeType,
|
private Mono<?> getAttributeMono(String attributeName, ResolvableType attributeType, Model model) {
|
||||||
MethodParameter param, BindingContext context, ServerWebExchange exchange) {
|
Object attribute = model.asMap().get(attributeName);
|
||||||
|
|
||||||
Object attribute = context.getModel().asMap().get(attributeName);
|
|
||||||
if (attribute == null) {
|
if (attribute == null) {
|
||||||
attribute = createAttribute(attributeName, attributeType, param, context, exchange);
|
attribute = BeanUtils.instantiateClass(attributeType.getRawClass());
|
||||||
}
|
}
|
||||||
if (attribute != null) {
|
ReactiveAdapter adapterFrom = getAdapterRegistry().getAdapter(null, attribute);
|
||||||
ReactiveAdapter adapterFrom = getAdapterRegistry().getAdapter(null, attribute);
|
if (adapterFrom != null) {
|
||||||
if (adapterFrom != null) {
|
Assert.isTrue(!adapterFrom.isMultiValue(), "Data binding supports single-value async types.");
|
||||||
Assert.isTrue(!adapterFrom.isMultiValue(), "Data binding supports single-value async types.");
|
return Mono.from(adapterFrom.toPublisher(attribute));
|
||||||
return Mono.from(adapterFrom.toPublisher(attribute));
|
}
|
||||||
}
|
else {
|
||||||
|
return Mono.justOrEmpty(attribute);
|
||||||
}
|
}
|
||||||
return Mono.justOrEmpty(attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasErrorsArgument(MethodParameter methodParam) {
|
||||||
protected Object createAttribute(String attributeName, Class<?> attributeType,
|
|
||||||
MethodParameter parameter, BindingContext context, ServerWebExchange exchange) {
|
|
||||||
|
|
||||||
return BeanUtils.instantiateClass(attributeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean checkErrorsArgument(MethodParameter methodParam) {
|
|
||||||
int i = methodParam.getParameterIndex();
|
int i = methodParam.getParameterIndex();
|
||||||
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
|
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
|
||||||
return paramTypes.length <= (i + 1) || !Errors.class.isAssignableFrom(paramTypes[i + 1]);
|
return paramTypes.length > i && Errors.class.isAssignableFrom(paramTypes[i + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void validateIfApplicable(WebExchangeDataBinder binder, MethodParameter parameter) {
|
private void validateIfApplicable(WebExchangeDataBinder binder, MethodParameter parameter) {
|
||||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||||
for (Annotation ann : annotations) {
|
for (Annotation ann : annotations) {
|
||||||
Validated validAnnot = AnnotationUtils.getAnnotation(ann, Validated.class);
|
Validated validAnnot = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||||
|
|||||||
@@ -43,9 +43,10 @@ public class PathVariableMapMethodArgumentResolver implements SyncHandlerMethodA
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
|
PathVariable annotation = parameter.getParameterAnnotation(PathVariable.class);
|
||||||
return (ann != null && (Map.class.isAssignableFrom(parameter.getParameterType()))
|
return (annotation != null &&
|
||||||
&& !StringUtils.hasText(ann.value()));
|
Map.class.isAssignableFrom(parameter.getParameterType()) &&
|
||||||
|
!StringUtils.hasText(annotation.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentRe
|
|||||||
public Mono<Object> resolveArgument(MethodParameter param, BindingContext bindingContext,
|
public Mono<Object> resolveArgument(MethodParameter param, BindingContext bindingContext,
|
||||||
ServerWebExchange exchange) {
|
ServerWebExchange exchange) {
|
||||||
|
|
||||||
boolean isRequired = param.getParameterAnnotation(RequestBody.class).required();
|
RequestBody annotation = param.getParameterAnnotation(RequestBody.class);
|
||||||
return readBody(param, isRequired, bindingContext, exchange);
|
return readBody(param, annotation.required(), bindingContext, exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,31 +55,26 @@ public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandle
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with {@link HttpMessageWriter}s and a
|
* Basic constructor with a default {@link ReactiveAdapterRegistry}.
|
||||||
* {@code RequestedContentTypeResolver}.
|
* @param writers writers for serializing to the response body
|
||||||
*
|
* @param resolver to determine the requested content type
|
||||||
* @param messageWriters writers for serializing to the response body stream
|
|
||||||
* @param contentTypeResolver for resolving the requested content type
|
|
||||||
*/
|
*/
|
||||||
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> writers,
|
||||||
RequestedContentTypeResolver contentTypeResolver) {
|
RequestedContentTypeResolver resolver) {
|
||||||
|
|
||||||
this(messageWriters, contentTypeResolver, new ReactiveAdapterRegistry());
|
this(writers, resolver, new ReactiveAdapterRegistry());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with an additional {@link ReactiveAdapterRegistry}.
|
* Constructor with an {@link ReactiveAdapterRegistry} instance.
|
||||||
*
|
* @param writers writers for serializing to the response body
|
||||||
* @param messageWriters writers for serializing to the response body stream
|
* @param resolver to determine the requested content type
|
||||||
* @param contentTypeResolver for resolving the requested content type
|
* @param registry for adaptation to reactive types
|
||||||
* @param adapterRegistry for adapting other reactive types (e.g. rx.Observable,
|
|
||||||
* rx.Single, etc.) to Flux or Mono
|
|
||||||
*/
|
*/
|
||||||
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> writers,
|
||||||
RequestedContentTypeResolver contentTypeResolver,
|
RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) {
|
||||||
ReactiveAdapterRegistry adapterRegistry) {
|
|
||||||
|
|
||||||
super(messageWriters, contentTypeResolver, adapterRegistry);
|
super(writers, resolver, registry);
|
||||||
setOrder(100);
|
setOrder(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,32 +82,11 @@ public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandle
|
|||||||
@Override
|
@Override
|
||||||
public boolean supports(HandlerResult result) {
|
public boolean supports(HandlerResult result) {
|
||||||
MethodParameter parameter = result.getReturnTypeSource();
|
MethodParameter parameter = result.getReturnTypeSource();
|
||||||
return hasResponseBodyAnnotation(parameter) && !isHttpEntityType(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasResponseBodyAnnotation(MethodParameter parameter) {
|
|
||||||
Class<?> containingClass = parameter.getContainingClass();
|
Class<?> containingClass = parameter.getContainingClass();
|
||||||
return (AnnotationUtils.findAnnotation(containingClass, ResponseBody.class) != null ||
|
return (AnnotationUtils.findAnnotation(containingClass, ResponseBody.class) != null ||
|
||||||
parameter.getMethodAnnotation(ResponseBody.class) != null);
|
parameter.getMethodAnnotation(ResponseBody.class) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isHttpEntityType(HandlerResult result) {
|
|
||||||
Class<?> rawClass = result.getReturnType().getRawClass();
|
|
||||||
if (HttpEntity.class.isAssignableFrom(rawClass)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(rawClass, result.getReturnValue());
|
|
||||||
if (adapter != null && !adapter.isNoValue()) {
|
|
||||||
ResolvableType genericType = result.getReturnType().getGeneric(0);
|
|
||||||
if (HttpEntity.class.isAssignableFrom(genericType.getRawClass())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
||||||
Object body = result.getReturnValue().orElse(null);
|
Object body = result.getReturnValue().orElse(null);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package org.springframework.web.reactive.result.method.annotation;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
@@ -54,78 +53,61 @@ public class ResponseEntityResultHandler extends AbstractMessageWriterResultHand
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with {@link HttpMessageWriter}s and a
|
* Basic constructor with a default {@link ReactiveAdapterRegistry}.
|
||||||
* {@code RequestedContentTypeResolver}.
|
* @param writers writers for serializing to the response body
|
||||||
*
|
* @param resolver to determine the requested content type
|
||||||
* @param messageWriters writers for serializing to the response body stream
|
|
||||||
* @param contentTypeResolver for resolving the requested content type
|
|
||||||
*/
|
*/
|
||||||
public ResponseEntityResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
public ResponseEntityResultHandler(List<HttpMessageWriter<?>> writers,
|
||||||
RequestedContentTypeResolver contentTypeResolver) {
|
RequestedContentTypeResolver resolver) {
|
||||||
|
|
||||||
this(messageWriters, contentTypeResolver, new ReactiveAdapterRegistry());
|
this(writers, resolver, new ReactiveAdapterRegistry());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with an additional {@link ReactiveAdapterRegistry}.
|
* Constructor with an {@link ReactiveAdapterRegistry} instance.
|
||||||
*
|
* @param writers writers for serializing to the response body
|
||||||
* @param messageWriters writers for serializing to the response body stream
|
* @param resolver to determine the requested content type
|
||||||
* @param contentTypeResolver for resolving the requested content type
|
* @param registry for adaptation to reactive types
|
||||||
* @param adapterRegistry for adapting other reactive types (e.g. rx.Observable,
|
|
||||||
* rx.Single, etc.) to Flux or Mono
|
|
||||||
*/
|
*/
|
||||||
public ResponseEntityResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
public ResponseEntityResultHandler(List<HttpMessageWriter<?>> writers,
|
||||||
RequestedContentTypeResolver contentTypeResolver,
|
RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) {
|
||||||
ReactiveAdapterRegistry adapterRegistry) {
|
|
||||||
|
|
||||||
super(messageWriters, contentTypeResolver, adapterRegistry);
|
super(writers, resolver, registry);
|
||||||
setOrder(0);
|
setOrder(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(HandlerResult result) {
|
public boolean supports(HandlerResult result) {
|
||||||
Class<?> returnType = result.getReturnType().getRawClass();
|
if (isSupportedType(result.getReturnType())) {
|
||||||
if (isSupportedType(returnType)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
ReactiveAdapter adapter = getAdapter(result);
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(returnType, result.getReturnValue());
|
return adapter != null && !adapter.isNoValue() &&
|
||||||
if (adapter != null && !adapter.isMultiValue() && !adapter.isNoValue()) {
|
isSupportedType(result.getReturnType().getGeneric(0));
|
||||||
ResolvableType genericType = result.getReturnType().getGeneric(0);
|
|
||||||
return isSupportedType(genericType.getRawClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSupportedType(Class<?> clazz) {
|
private boolean isSupportedType(ResolvableType type) {
|
||||||
|
Class<?> clazz = type.getRawClass();
|
||||||
return (HttpEntity.class.isAssignableFrom(clazz) && !RequestEntity.class.isAssignableFrom(clazz));
|
return (HttpEntity.class.isAssignableFrom(clazz) && !RequestEntity.class.isAssignableFrom(clazz));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
||||||
ResolvableType returnType = result.getReturnType();
|
|
||||||
MethodParameter bodyType;
|
|
||||||
|
|
||||||
Mono<?> returnValueMono;
|
Mono<?> returnValueMono;
|
||||||
Optional<Object> optionalValue = result.getReturnValue();
|
MethodParameter bodyParameter;
|
||||||
|
ReactiveAdapter adapter = getAdapter(result);
|
||||||
Class<?> rawClass = returnType.getRawClass();
|
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(rawClass, optionalValue);
|
|
||||||
|
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
Assert.isTrue(!adapter.isMultiValue(), "Only a single ResponseEntity supported");
|
Assert.isTrue(!adapter.isMultiValue(), "Only a single ResponseEntity supported");
|
||||||
returnValueMono = Mono.from(adapter.toPublisher(optionalValue));
|
returnValueMono = Mono.from(adapter.toPublisher(result.getReturnValue()));
|
||||||
bodyType = new MethodParameter(result.getReturnTypeSource());
|
bodyParameter = result.getReturnTypeSource().nested().nested();
|
||||||
bodyType.increaseNestingLevel();
|
|
||||||
bodyType.increaseNestingLevel();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
returnValueMono = Mono.justOrEmpty(optionalValue);
|
returnValueMono = Mono.justOrEmpty(result.getReturnValue());
|
||||||
bodyType = new MethodParameter(result.getReturnTypeSource());
|
bodyParameter = result.getReturnTypeSource().nested();
|
||||||
bodyType.increaseNestingLevel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValueMono.then(returnValue -> {
|
return returnValueMono.then(returnValue -> {
|
||||||
@@ -156,7 +138,7 @@ public class ResponseEntityResultHandler extends AbstractMessageWriterResultHand
|
|||||||
return exchange.getResponse().setComplete();
|
return exchange.getResponse().setComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
return writeBody(httpEntity.getBody(), bodyType, exchange);
|
return writeBody(httpEntity.getBody(), bodyParameter, exchange);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
@@ -94,29 +93,27 @@ public class ViewResolutionResultHandler extends AbstractHandlerResultHandler
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with {@link ViewResolver}s and a {@link RequestedContentTypeResolver}.
|
* Basic constructor with a default {@link ReactiveAdapterRegistry}.
|
||||||
* @param resolvers the resolver to use
|
* @param viewResolvers the resolver to use
|
||||||
* @param contentTypeResolver for resolving the requested content type
|
* @param contentTypeResolver to determine the requested content type
|
||||||
*/
|
*/
|
||||||
public ViewResolutionResultHandler(List<ViewResolver> resolvers,
|
public ViewResolutionResultHandler(List<ViewResolver> viewResolvers,
|
||||||
RequestedContentTypeResolver contentTypeResolver) {
|
RequestedContentTypeResolver contentTypeResolver) {
|
||||||
|
|
||||||
this(resolvers, contentTypeResolver, new ReactiveAdapterRegistry());
|
this(viewResolvers, contentTypeResolver, new ReactiveAdapterRegistry());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with {@code ViewResolver}s tand a {@code ConversionService}.
|
* Constructor with an {@link ReactiveAdapterRegistry} instance.
|
||||||
* @param resolvers the resolver to use
|
* @param viewResolvers the view resolver to use
|
||||||
* @param contentTypeResolver for resolving the requested content type
|
* @param contentTypeResolver to determine the requested content type
|
||||||
* @param adapterRegistry for adapting from other reactive types (e.g.
|
* @param registry for adaptation to reactive types
|
||||||
* rx.Single) to Mono
|
|
||||||
*/
|
*/
|
||||||
public ViewResolutionResultHandler(List<ViewResolver> resolvers,
|
public ViewResolutionResultHandler(List<ViewResolver> viewResolvers,
|
||||||
RequestedContentTypeResolver contentTypeResolver,
|
RequestedContentTypeResolver contentTypeResolver, ReactiveAdapterRegistry registry) {
|
||||||
ReactiveAdapterRegistry adapterRegistry) {
|
|
||||||
|
|
||||||
super(contentTypeResolver, adapterRegistry);
|
super(contentTypeResolver, registry);
|
||||||
this.viewResolvers.addAll(resolvers);
|
this.viewResolvers.addAll(viewResolvers);
|
||||||
AnnotationAwareOrderComparator.sort(this.viewResolvers);
|
AnnotationAwareOrderComparator.sort(this.viewResolvers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,113 +145,97 @@ public class ViewResolutionResultHandler extends AbstractHandlerResultHandler
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(HandlerResult result) {
|
public boolean supports(HandlerResult result) {
|
||||||
Class<?> clazz = result.getReturnType().getRawClass();
|
if (hasModelAnnotation(result.getReturnTypeSource())) {
|
||||||
if (hasModelAttributeAnnotation(result)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Optional<Object> optional = result.getReturnValue();
|
Class<?> type = result.getReturnType().getRawClass();
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(clazz, optional);
|
ReactiveAdapter adapter = getAdapter(result);
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
if (adapter.isNoValue()) {
|
if (adapter.isNoValue()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
type = result.getReturnType().getGeneric(0).getRawClass();
|
||||||
clazz = result.getReturnType().getGeneric(0).getRawClass();
|
|
||||||
return isSupportedType(clazz);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (isSupportedType(clazz)) {
|
return (CharSequence.class.isAssignableFrom(type) || View.class.isAssignableFrom(type) ||
|
||||||
return true;
|
Model.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type) ||
|
||||||
}
|
!BeanUtils.isSimpleProperty(type));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasModelAttributeAnnotation(HandlerResult result) {
|
private boolean hasModelAnnotation(MethodParameter parameter) {
|
||||||
MethodParameter returnType = result.getReturnTypeSource();
|
return parameter.hasMethodAnnotation(ModelAttribute.class);
|
||||||
return returnType.hasMethodAnnotation(ModelAttribute.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSupportedType(Class<?> clazz) {
|
|
||||||
return (CharSequence.class.isAssignableFrom(clazz) || View.class.isAssignableFrom(clazz) ||
|
|
||||||
Model.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) ||
|
|
||||||
!BeanUtils.isSimpleProperty(clazz));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
||||||
|
|
||||||
Mono<Object> returnValueMono;
|
Mono<Object> valueMono;
|
||||||
ResolvableType elementType;
|
ResolvableType valueType;
|
||||||
ResolvableType parameterType = result.getReturnType();
|
ReactiveAdapter adapter = getAdapter(result);
|
||||||
|
|
||||||
Optional<Object> optional = result.getReturnValue();
|
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(parameterType.getRawClass(), optional);
|
|
||||||
|
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
Assert.isTrue(!adapter.isMultiValue(), "Only single-value async return type supported.");
|
Assert.isTrue(!adapter.isMultiValue(), "Only single-value async return type supported.");
|
||||||
returnValueMono = optional
|
valueMono = result.getReturnValue()
|
||||||
.map(value -> Mono.from(adapter.toPublisher(value)))
|
.map(value -> Mono.from(adapter.toPublisher(value))).orElse(Mono.empty());
|
||||||
.orElse(Mono.empty());
|
valueType = adapter.isNoValue() ?
|
||||||
elementType = !adapter.isNoValue() ?
|
ResolvableType.forClass(Void.class) : result.getReturnType().getGeneric(0);
|
||||||
parameterType.getGeneric(0) : ResolvableType.forClass(Void.class);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
returnValueMono = Mono.justOrEmpty(result.getReturnValue());
|
valueMono = Mono.justOrEmpty(result.getReturnValue());
|
||||||
elementType = parameterType;
|
valueType = result.getReturnType();
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValueMono
|
return valueMono
|
||||||
.otherwiseIfEmpty(exchange.isNotModified() ? Mono.empty() : NO_VALUE_MONO)
|
.otherwiseIfEmpty(exchange.isNotModified() ? Mono.empty() : NO_VALUE_MONO)
|
||||||
.then(returnValue -> {
|
.then(returnValue -> {
|
||||||
|
|
||||||
Mono<List<View>> viewsMono;
|
Mono<List<View>> viewsMono;
|
||||||
Model model = result.getModel();
|
Model model = result.getModel();
|
||||||
|
MethodParameter parameter = result.getReturnTypeSource();
|
||||||
|
|
||||||
Locale acceptLocale = exchange.getRequest().getHeaders().getAcceptLanguageAsLocale();
|
Locale acceptLocale = exchange.getRequest().getHeaders().getAcceptLanguageAsLocale();
|
||||||
Locale locale = acceptLocale != null ? acceptLocale : Locale.getDefault();
|
Locale locale = acceptLocale != null ? acceptLocale : Locale.getDefault();
|
||||||
|
|
||||||
Class<?> clazz = elementType.getRawClass();
|
Class<?> clazz = valueType.getRawClass();
|
||||||
if (clazz == null) {
|
if (clazz == null) {
|
||||||
clazz = returnValue.getClass();
|
clazz = returnValue.getClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnValue == NO_VALUE || Void.class.equals(clazz) || void.class.equals(clazz)) {
|
if (returnValue == NO_VALUE || Void.class.equals(clazz) || void.class.equals(clazz)) {
|
||||||
viewsMono = resolveViews(getDefaultViewName(result, exchange), locale);
|
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
|
||||||
}
|
}
|
||||||
else if (Model.class.isAssignableFrom(clazz)) {
|
else if (Model.class.isAssignableFrom(clazz)) {
|
||||||
model.addAllAttributes(((Model) returnValue).asMap());
|
model.addAllAttributes(((Model) returnValue).asMap());
|
||||||
viewsMono = resolveViews(getDefaultViewName(result, exchange), locale);
|
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
|
||||||
}
|
}
|
||||||
else if (Map.class.isAssignableFrom(clazz)) {
|
else if (Map.class.isAssignableFrom(clazz)) {
|
||||||
model.addAllAttributes((Map<String, ?>) returnValue);
|
model.addAllAttributes((Map<String, ?>) returnValue);
|
||||||
viewsMono = resolveViews(getDefaultViewName(result, exchange), locale);
|
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
|
||||||
}
|
}
|
||||||
else if (View.class.isAssignableFrom(clazz)) {
|
else if (View.class.isAssignableFrom(clazz)) {
|
||||||
viewsMono = Mono.just(Collections.singletonList((View) returnValue));
|
viewsMono = Mono.just(Collections.singletonList((View) returnValue));
|
||||||
}
|
}
|
||||||
else if (CharSequence.class.isAssignableFrom(clazz) && !hasModelAttributeAnnotation(result)) {
|
else if (CharSequence.class.isAssignableFrom(clazz) && !hasModelAnnotation(parameter)) {
|
||||||
viewsMono = resolveViews(returnValue.toString(), locale);
|
viewsMono = resolveViews(returnValue.toString(), locale);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String name = getNameForReturnValue(clazz, result.getReturnTypeSource());
|
String name = getNameForReturnValue(clazz, parameter);
|
||||||
model.addAttribute(name, returnValue);
|
model.addAttribute(name, returnValue);
|
||||||
viewsMono = resolveViews(getDefaultViewName(result, exchange), locale);
|
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolveAsyncAttributes(model.asMap())
|
return resolveAsyncAttributes(model.asMap())
|
||||||
.doOnSuccess(aVoid -> addBindingResult(result, exchange))
|
.doOnSuccess(aVoid -> addBindingResult(result.getBindingContext(), exchange))
|
||||||
.then(viewsMono)
|
.then(viewsMono)
|
||||||
.then(views -> render(views, model.asMap(), exchange));
|
.then(views -> render(views, model.asMap(), exchange));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a default view name when a controller leaves the view unspecified.
|
* Select a default view name when a controller did not specify it.
|
||||||
* The default implementation strips the leading and trailing slash from the
|
* Use the request path the leading and trailing slash stripped.
|
||||||
* as well as any extension and uses that as the view name.
|
|
||||||
*/
|
*/
|
||||||
protected String getDefaultViewName(HandlerResult result, ServerWebExchange exchange) {
|
private String getDefaultViewName(ServerWebExchange exchange) {
|
||||||
String path = this.pathHelper.getLookupPathForRequest(exchange);
|
String path = this.pathHelper.getLookupPathForRequest(exchange);
|
||||||
if (path.startsWith("/")) {
|
if (path.startsWith("/")) {
|
||||||
path = path.substring(1);
|
path = path.substring(1);
|
||||||
@@ -332,8 +313,7 @@ public class ViewResolutionResultHandler extends AbstractHandlerResultHandler
|
|||||||
.then();
|
.then();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBindingResult(HandlerResult result, ServerWebExchange exchange) {
|
private void addBindingResult(BindingContext context, ServerWebExchange exchange) {
|
||||||
BindingContext context = result.getBindingContext();
|
|
||||||
Map<String, Object> model = context.getModel().asMap();
|
Map<String, Object> model = context.getModel().asMap();
|
||||||
model.keySet().stream()
|
model.keySet().stream()
|
||||||
.filter(name -> isBindingCandidate(name, model.get(name)))
|
.filter(name -> isBindingCandidate(name, model.get(name)))
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import org.springframework.http.codec.DecoderHttpMessageReader;
|
|||||||
import org.springframework.http.codec.HttpMessageReader;
|
import org.springframework.http.codec.HttpMessageReader;
|
||||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.web.reactive.BindingContext;
|
import org.springframework.web.reactive.BindingContext;
|
||||||
import org.springframework.web.method.ResolvableMethod;
|
import org.springframework.web.method.ResolvableMethod;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
@@ -83,23 +84,26 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supports() throws Exception {
|
public void supports() throws Exception {
|
||||||
testSupports(httpEntityType(String.class));
|
testSupports(this.testMethod.arg(httpEntityType(String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(Mono.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(Mono.class, String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(Single.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(Single.class, String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(io.reactivex.Single.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(io.reactivex.Single.class, String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(Maybe.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(Maybe.class, String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(CompletableFuture.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(CompletableFuture.class, String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(Flux.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(Flux.class, String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(Observable.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(Observable.class, String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(io.reactivex.Observable.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(io.reactivex.Observable.class, String.class)));
|
||||||
testSupports(httpEntityType(forClassWithGenerics(Flowable.class, String.class)));
|
testSupports(this.testMethod.arg(httpEntityType(Flowable.class, String.class)));
|
||||||
testSupports(forClassWithGenerics(RequestEntity.class, String.class));
|
testSupports(this.testMethod.arg(forClassWithGenerics(RequestEntity.class, String.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testSupports(MethodParameter parameter) {
|
||||||
|
assertTrue(this.resolver.supportsParameter(parameter));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void doesNotSupport() throws Exception {
|
public void doesNotSupport() throws Exception {
|
||||||
ResolvableType type = ResolvableType.forClassWithGenerics(Mono.class, String.class);
|
assertFalse(this.resolver.supportsParameter(this.testMethod.arg(Mono.class, String.class)));
|
||||||
assertFalse(this.resolver.supportsParameter(this.testMethod.arg(type)));
|
|
||||||
assertFalse(this.resolver.supportsParameter(this.testMethod.arg(String.class)));
|
assertFalse(this.resolver.supportsParameter(this.testMethod.arg(String.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +117,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithMono() throws Exception {
|
public void emptyBodyWithMono() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Mono.class, String.class));
|
ResolvableType type = httpEntityType(Mono.class, String.class);
|
||||||
HttpEntity<Mono<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<Mono<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
StepVerifier.create(entity.getBody()).expectNextCount(0).expectComplete().verify();
|
StepVerifier.create(entity.getBody()).expectNextCount(0).expectComplete().verify();
|
||||||
@@ -121,7 +125,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithFlux() throws Exception {
|
public void emptyBodyWithFlux() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Flux.class, String.class));
|
ResolvableType type = httpEntityType(Flux.class, String.class);
|
||||||
HttpEntity<Flux<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<Flux<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
StepVerifier.create(entity.getBody()).expectNextCount(0).expectComplete().verify();
|
StepVerifier.create(entity.getBody()).expectNextCount(0).expectComplete().verify();
|
||||||
@@ -129,7 +133,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithSingle() throws Exception {
|
public void emptyBodyWithSingle() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Single.class, String.class));
|
ResolvableType type = httpEntityType(Single.class, String.class);
|
||||||
HttpEntity<Single<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<Single<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
StepVerifier.create(RxReactiveStreams.toPublisher(entity.getBody()))
|
StepVerifier.create(RxReactiveStreams.toPublisher(entity.getBody()))
|
||||||
@@ -140,7 +144,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithRxJava2Single() throws Exception {
|
public void emptyBodyWithRxJava2Single() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(io.reactivex.Single.class, String.class));
|
ResolvableType type = httpEntityType(io.reactivex.Single.class, String.class);
|
||||||
HttpEntity<io.reactivex.Single<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<io.reactivex.Single<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
StepVerifier.create(entity.getBody().toFlowable())
|
StepVerifier.create(entity.getBody().toFlowable())
|
||||||
@@ -151,7 +155,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithRxJava2Maybe() throws Exception {
|
public void emptyBodyWithRxJava2Maybe() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Maybe.class, String.class));
|
ResolvableType type = httpEntityType(Maybe.class, String.class);
|
||||||
HttpEntity<Maybe<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<Maybe<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
StepVerifier.create(entity.getBody().toFlowable())
|
StepVerifier.create(entity.getBody().toFlowable())
|
||||||
@@ -162,7 +166,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithObservable() throws Exception {
|
public void emptyBodyWithObservable() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Observable.class, String.class));
|
ResolvableType type = httpEntityType(Observable.class, String.class);
|
||||||
HttpEntity<Observable<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<Observable<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
StepVerifier.create(RxReactiveStreams.toPublisher(entity.getBody()))
|
StepVerifier.create(RxReactiveStreams.toPublisher(entity.getBody()))
|
||||||
@@ -173,7 +177,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithRxJava2Observable() throws Exception {
|
public void emptyBodyWithRxJava2Observable() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(io.reactivex.Observable.class, String.class));
|
ResolvableType type = httpEntityType(io.reactivex.Observable.class, String.class);
|
||||||
HttpEntity<io.reactivex.Observable<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<io.reactivex.Observable<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
StepVerifier.create(entity.getBody().toFlowable(BackpressureStrategy.BUFFER))
|
StepVerifier.create(entity.getBody().toFlowable(BackpressureStrategy.BUFFER))
|
||||||
@@ -184,7 +188,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithFlowable() throws Exception {
|
public void emptyBodyWithFlowable() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Flowable.class, String.class));
|
ResolvableType type = httpEntityType(Flowable.class, String.class);
|
||||||
HttpEntity<Flowable<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<Flowable<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
StepVerifier.create(entity.getBody())
|
StepVerifier.create(entity.getBody())
|
||||||
@@ -195,7 +199,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBodyWithCompletableFuture() throws Exception {
|
public void emptyBodyWithCompletableFuture() throws Exception {
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(CompletableFuture.class, String.class));
|
ResolvableType type = httpEntityType(CompletableFuture.class, String.class);
|
||||||
HttpEntity<CompletableFuture<String>> entity = resolveValueWithEmptyBody(type);
|
HttpEntity<CompletableFuture<String>> entity = resolveValueWithEmptyBody(type);
|
||||||
|
|
||||||
entity.getBody().whenComplete((body, ex) -> {
|
entity.getBody().whenComplete((body, ex) -> {
|
||||||
@@ -217,7 +221,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
@Test
|
@Test
|
||||||
public void httpEntityWithMonoBody() throws Exception {
|
public void httpEntityWithMonoBody() throws Exception {
|
||||||
String body = "line1";
|
String body = "line1";
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Mono.class, String.class));
|
ResolvableType type = httpEntityType(Mono.class, String.class);
|
||||||
HttpEntity<Mono<String>> httpEntity = resolveValue(type, body);
|
HttpEntity<Mono<String>> httpEntity = resolveValue(type, body);
|
||||||
|
|
||||||
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
||||||
@@ -227,7 +231,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
@Test
|
@Test
|
||||||
public void httpEntityWithSingleBody() throws Exception {
|
public void httpEntityWithSingleBody() throws Exception {
|
||||||
String body = "line1";
|
String body = "line1";
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Single.class, String.class));
|
ResolvableType type = httpEntityType(Single.class, String.class);
|
||||||
HttpEntity<Single<String>> httpEntity = resolveValue(type, body);
|
HttpEntity<Single<String>> httpEntity = resolveValue(type, body);
|
||||||
|
|
||||||
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
||||||
@@ -237,7 +241,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
@Test
|
@Test
|
||||||
public void httpEntityWithRxJava2SingleBody() throws Exception {
|
public void httpEntityWithRxJava2SingleBody() throws Exception {
|
||||||
String body = "line1";
|
String body = "line1";
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(io.reactivex.Single.class, String.class));
|
ResolvableType type = httpEntityType(io.reactivex.Single.class, String.class);
|
||||||
HttpEntity<io.reactivex.Single<String>> httpEntity = resolveValue(type, body);
|
HttpEntity<io.reactivex.Single<String>> httpEntity = resolveValue(type, body);
|
||||||
|
|
||||||
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
||||||
@@ -247,7 +251,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
@Test
|
@Test
|
||||||
public void httpEntityWithRxJava2MaybeBody() throws Exception {
|
public void httpEntityWithRxJava2MaybeBody() throws Exception {
|
||||||
String body = "line1";
|
String body = "line1";
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Maybe.class, String.class));
|
ResolvableType type = httpEntityType(Maybe.class, String.class);
|
||||||
HttpEntity<Maybe<String>> httpEntity = resolveValue(type, body);
|
HttpEntity<Maybe<String>> httpEntity = resolveValue(type, body);
|
||||||
|
|
||||||
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
||||||
@@ -257,7 +261,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
@Test
|
@Test
|
||||||
public void httpEntityWithCompletableFutureBody() throws Exception {
|
public void httpEntityWithCompletableFutureBody() throws Exception {
|
||||||
String body = "line1";
|
String body = "line1";
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(CompletableFuture.class, String.class));
|
ResolvableType type = httpEntityType(CompletableFuture.class, String.class);
|
||||||
HttpEntity<CompletableFuture<String>> httpEntity = resolveValue(type, body);
|
HttpEntity<CompletableFuture<String>> httpEntity = resolveValue(type, body);
|
||||||
|
|
||||||
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
||||||
@@ -267,7 +271,7 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
@Test
|
@Test
|
||||||
public void httpEntityWithFluxBody() throws Exception {
|
public void httpEntityWithFluxBody() throws Exception {
|
||||||
String body = "line1\nline2\nline3\n";
|
String body = "line1\nline2\nline3\n";
|
||||||
ResolvableType type = httpEntityType(forClassWithGenerics(Flux.class, String.class));
|
ResolvableType type = httpEntityType(Flux.class, String.class);
|
||||||
HttpEntity<Flux<String>> httpEntity = resolveValue(type, body);
|
HttpEntity<Flux<String>> httpEntity = resolveValue(type, body);
|
||||||
|
|
||||||
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
assertEquals(this.request.getHeaders(), httpEntity.getHeaders());
|
||||||
@@ -292,18 +296,13 @@ public class HttpEntityArgumentResolverTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ResolvableType httpEntityType(Class<?> bodyType) {
|
private ResolvableType httpEntityType(Class<?> bodyType, Class<?>... generics) {
|
||||||
return httpEntityType(ResolvableType.forClass(bodyType));
|
return ResolvableType.forClassWithGenerics(HttpEntity.class,
|
||||||
|
ObjectUtils.isEmpty(generics) ?
|
||||||
|
ResolvableType.forClass(bodyType) :
|
||||||
|
ResolvableType.forClassWithGenerics(bodyType, generics));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolvableType httpEntityType(ResolvableType type) {
|
|
||||||
return forClassWithGenerics(HttpEntity.class, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testSupports(ResolvableType type) {
|
|
||||||
MethodParameter parameter = this.testMethod.arg(type);
|
|
||||||
assertTrue(this.resolver.supportsParameter(parameter));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T resolveValue(ResolvableType type, String body) {
|
private <T> T resolveValue(ResolvableType type, String body) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.web.reactive.result.method.annotation;
|
package org.springframework.web.reactive.result.method.annotation;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -27,7 +28,6 @@ import rx.Single;
|
|||||||
|
|
||||||
import org.springframework.core.codec.ByteBufferEncoder;
|
import org.springframework.core.codec.ByteBufferEncoder;
|
||||||
import org.springframework.core.codec.CharSequenceEncoder;
|
import org.springframework.core.codec.CharSequenceEncoder;
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||||
import org.springframework.http.codec.HttpMessageWriter;
|
import org.springframework.http.codec.HttpMessageWriter;
|
||||||
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||||
@@ -41,7 +41,10 @@ import org.springframework.web.reactive.HandlerResult;
|
|||||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
|
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.springframework.web.method.ResolvableMethod.on;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link ResponseBodyResultHandler}.When adding a test also
|
* Unit tests for {@link ResponseBodyResultHandler}.When adding a test also
|
||||||
@@ -75,22 +78,42 @@ public class ResponseBodyResultHandlerTests {
|
|||||||
@Test
|
@Test
|
||||||
public void supports() throws NoSuchMethodException {
|
public void supports() throws NoSuchMethodException {
|
||||||
Object controller = new TestController();
|
Object controller = new TestController();
|
||||||
testSupports(controller, "handleToString", true);
|
Method method;
|
||||||
testSupports(controller, "doWork", false);
|
|
||||||
|
|
||||||
controller = new TestRestController();
|
method = on(TestController.class).annotPresent(ResponseBody.class).resolveMethod();
|
||||||
testSupports(controller, "handleToString", true);
|
testSupports(controller, method);
|
||||||
testSupports(controller, "handleToMonoString", true);
|
|
||||||
testSupports(controller, "handleToSingleString", true);
|
method = on(TestController.class).annotNotPresent(ResponseBody.class).resolveMethod();
|
||||||
testSupports(controller, "handleToCompletable", true);
|
HandlerResult handlerResult = getHandlerResult(controller, method);
|
||||||
testSupports(controller, "handleToResponseEntity", false);
|
assertFalse(this.resultHandler.supports(handlerResult));
|
||||||
testSupports(controller, "handleToMonoResponseEntity", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSupports(Object controller, String method, boolean result) throws NoSuchMethodException {
|
@Test
|
||||||
HandlerMethod hm = handlerMethod(controller, method);
|
public void supportsRestController() throws NoSuchMethodException {
|
||||||
HandlerResult handlerResult = new HandlerResult(hm, null, hm.getReturnType());
|
Object controller = new TestRestController();
|
||||||
assertEquals(result, this.resultHandler.supports(handlerResult));
|
Method method;
|
||||||
|
|
||||||
|
method = on(TestRestController.class).returning(String.class).resolveMethod();
|
||||||
|
testSupports(controller, method);
|
||||||
|
|
||||||
|
method = on(TestRestController.class).returning(Mono.class, String.class).resolveMethod();
|
||||||
|
testSupports(controller, method);
|
||||||
|
|
||||||
|
method = on(TestRestController.class).returning(Single.class, String.class).resolveMethod();
|
||||||
|
testSupports(controller, method);
|
||||||
|
|
||||||
|
method = on(TestRestController.class).returning(Completable.class).resolveMethod();
|
||||||
|
testSupports(controller, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testSupports(Object controller, Method method) {
|
||||||
|
HandlerResult handlerResult = getHandlerResult(controller, method);
|
||||||
|
assertTrue(this.resultHandler.supports(handlerResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HandlerResult getHandlerResult(Object controller, Method method) {
|
||||||
|
HandlerMethod handlerMethod = new HandlerMethod(controller, method);
|
||||||
|
return new HandlerResult(handlerMethod, null, handlerMethod.getReturnType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -99,10 +122,6 @@ public class ResponseBodyResultHandlerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private HandlerMethod handlerMethod(Object controller, String method) throws NoSuchMethodException {
|
|
||||||
return new HandlerMethod(controller, controller.getClass().getMethod(method));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -125,14 +144,6 @@ public class ResponseBodyResultHandlerTests {
|
|||||||
public Completable handleToCompletable() {
|
public Completable handleToCompletable() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseEntity<String> handleToResponseEntity() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mono<ResponseEntity<String>> handleToMonoResponseEntity() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user