Add RequestBodyAdvice
RequestBodyAdvice is analogous to ResponseBodyAdvice (added in 4.1) but for intercepting for reading the request with an HttpMessageConverter for resolving an @RequestBody or an HttpEntity method argument. Issue: SPR-12501
This commit is contained in:
parent
073c176400
commit
0556ed4f16
|
|
@ -24,6 +24,7 @@ import java.util.Collections;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
|
@ -62,11 +63,27 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
|||
|
||||
protected final List<MediaType> allSupportedMediaTypes;
|
||||
|
||||
private final RequestResponseBodyAdviceChain advice;
|
||||
|
||||
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.messageConverters = messageConverters;
|
||||
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
|
||||
|
||||
/**
|
||||
* Basic constructor with converters only.
|
||||
*/
|
||||
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters) {
|
||||
this(converters, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with converters and {@code Request~} and {@code ResponseBodyAdvice}.
|
||||
* @since 4.2
|
||||
*/
|
||||
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
|
||||
List<Object> requestResponseBodyAdvice) {
|
||||
|
||||
Assert.notEmpty(converters, "'messageConverters' must not be empty");
|
||||
this.messageConverters = converters;
|
||||
this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
|
||||
this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -85,6 +102,15 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured {@link RequestBodyAdvice} and
|
||||
* {@link RequestBodyAdvice} where each instance may be wrapped as a
|
||||
* {@link org.springframework.web.method.ControllerAdviceBean ControllerAdviceBean}.
|
||||
*/
|
||||
protected RequestResponseBodyAdviceChain getAdvice() {
|
||||
return this.advice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the method argument value of the expected parameter type by
|
||||
* reading from the given request.
|
||||
|
|
@ -108,7 +134,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
|||
* from the given HttpInputMessage.
|
||||
* @param <T> the expected type of the argument value to be created
|
||||
* @param inputMessage the HTTP input message representing the current request
|
||||
* @param methodParam the method parameter descriptor (may be {@code null})
|
||||
* @param param the method parameter descriptor (may be {@code null})
|
||||
* @param targetType the type of object to create, not necessarily the same as
|
||||
* the method parameter type (e.g. for {@code HttpEntity<String>} method
|
||||
* parameter the target type is String)
|
||||
|
|
@ -118,7 +144,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
|
||||
MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
MethodParameter param, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
MediaType contentType;
|
||||
try {
|
||||
|
|
@ -131,32 +157,35 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
|||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
|
||||
Class<?> contextClass = (methodParam != null ? methodParam.getContainingClass() : null);
|
||||
Class<?> contextClass = (param != null ? param.getContainingClass() : null);
|
||||
Class<T> targetClass = (targetType instanceof Class<?> ? (Class<T>) targetType : null);
|
||||
if (targetClass == null) {
|
||||
ResolvableType resolvableType = (methodParam != null ?
|
||||
ResolvableType.forMethodParameter(methodParam) : ResolvableType.forType(targetType));
|
||||
ResolvableType resolvableType = (param != null ?
|
||||
ResolvableType.forMethodParameter(param) : ResolvableType.forType(targetType));
|
||||
targetClass = (Class<T>) resolvableType.resolve();
|
||||
}
|
||||
|
||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
|
||||
if (converter instanceof GenericHttpMessageConverter) {
|
||||
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
|
||||
if (genericConverter.canRead(targetType, contextClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + targetType + "] as \"" +
|
||||
contentType + "\" using [" + converter + "]");
|
||||
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
|
||||
}
|
||||
return genericConverter.read(targetType, contextClass, inputMessage);
|
||||
inputMessage = getAdvice().beforeBodyRead(inputMessage, param, targetType, converterType);
|
||||
T body = (T) genericConverter.read(targetType, contextClass, inputMessage);
|
||||
return getAdvice().afterBodyRead(body, inputMessage, param, targetType, converterType);
|
||||
}
|
||||
}
|
||||
else if (targetClass != null) {
|
||||
if (converter.canRead(targetClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
|
||||
contentType + "\" using [" + converter + "]");
|
||||
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
|
||||
}
|
||||
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
|
||||
inputMessage = getAdvice().beforeBodyRead(inputMessage, param, targetType, converterType);
|
||||
T body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
|
||||
return getAdvice().afterBodyRead(body, inputMessage, param, targetType, converterType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -22,6 +22,7 @@ import java.util.Collections;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
|
@ -54,31 +55,25 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
|||
|
||||
private final ContentNegotiationManager contentNegotiationManager;
|
||||
|
||||
private final ResponseBodyAdviceChain adviceChain;
|
||||
|
||||
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this(messageConverters, null);
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters) {
|
||||
this(converters, null);
|
||||
}
|
||||
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager manager) {
|
||||
this(messageConverters, manager, null);
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
|
||||
ContentNegotiationManager contentNegotiationManager) {
|
||||
|
||||
this(converters, contentNegotiationManager, null);
|
||||
}
|
||||
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager manager, List<Object> responseBodyAdvice) {
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
|
||||
ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
|
||||
|
||||
super(messageConverters);
|
||||
super(converters, requestResponseBodyAdvice);
|
||||
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
|
||||
this.adviceChain = new ResponseBodyAdviceChain(responseBodyAdvice);
|
||||
}
|
||||
|
||||
|
||||
protected ResponseBodyAdviceChain getAdviceChain() {
|
||||
return this.adviceChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}.
|
||||
* @param webRequest the web request to create an output message from
|
||||
|
|
@ -155,13 +150,14 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
|||
selectedMediaType = selectedMediaType.removeQualityValue();
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
|
||||
returnValue = this.adviceChain.invoke(returnValue, returnType, selectedMediaType,
|
||||
(Class<HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
|
||||
returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType,
|
||||
(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
|
||||
inputMessage, outputMessage);
|
||||
if (returnValue != null) {
|
||||
((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
|
||||
messageConverter + "]");
|
||||
logger.debug("Written [" + returnValue + "] as \"" +
|
||||
selectedMediaType + "\" using [" + messageConverter + "]");
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -54,20 +54,47 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
|||
*/
|
||||
public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {
|
||||
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
|
||||
super(messageConverters);
|
||||
|
||||
/**
|
||||
* Basic constructor with converters only. Suitable for resolving
|
||||
* {@code HttpEntity}. For handling {@code ResponseEntity} consider also
|
||||
* providing a {@code ContentNegotiationManager}.
|
||||
*/
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters) {
|
||||
super(converters);
|
||||
}
|
||||
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager contentNegotiationManager) {
|
||||
/**
|
||||
* Basic constructor with converters and {@code ContentNegotiationManager}.
|
||||
* Suitable for resolving {@code HttpEntity} and handling {@code ResponseEntity}
|
||||
* without {@code Request~} or {@code ResponseBodyAdvice}.
|
||||
*/
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
|
||||
ContentNegotiationManager manager) {
|
||||
|
||||
super(messageConverters, contentNegotiationManager);
|
||||
super(converters, manager);
|
||||
}
|
||||
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager contentNegotiationManager, List<Object> responseBodyAdvice) {
|
||||
/**
|
||||
* Complete constructor for resolving {@code HttpEntity} method arguments.
|
||||
* For handling {@code ResponseEntity} consider also providing a
|
||||
* {@code ContentNegotiationManager}.
|
||||
* @since 4.2
|
||||
*/
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
|
||||
List<Object> requestResponseBodyAdvice) {
|
||||
|
||||
super(messageConverters, contentNegotiationManager, responseBodyAdvice);
|
||||
super(converters, null, requestResponseBodyAdvice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete constructor for resolving {@code HttpEntity} and handling
|
||||
* {@code ResponseEntity}.
|
||||
*/
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
|
||||
ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
|
||||
|
||||
super(converters, manager, requestResponseBodyAdvice);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
|
||||
|
||||
/**
|
||||
* Allows customizing the request before its body is read and converted into an
|
||||
* Object and also allows the resulting Object before it is passed into a
|
||||
* controller method as an {code @RequestBody} or an {@code HttpEntity} method
|
||||
* argument.
|
||||
*
|
||||
* <p>Implementations of this contract may be registered directly with the
|
||||
* {@code RequestMappingHandlerAdapter} or more likely annotated with
|
||||
* {@code @ControllerAdvice} in which case they are auto-detected.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.2
|
||||
*/
|
||||
public interface RequestBodyAdvice {
|
||||
|
||||
/**
|
||||
* Invoked first to determine if this interceptor applies.
|
||||
* @param methodParameter the method parameter
|
||||
* @param targetType the target type, not necessarily the same as the method
|
||||
* parameter type, e.g. for {@code HttpEntity<String>}.
|
||||
* @param converterType the selected converter type
|
||||
* @return whether this interceptor should be invoked or not
|
||||
*/
|
||||
boolean supports(MethodParameter methodParameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType);
|
||||
|
||||
/**
|
||||
* Invoked second (and last) if the body is empty.
|
||||
* @param body set to {@code null} before the first advice is called
|
||||
* @param inputMessage the request
|
||||
* @param parameter the method parameter
|
||||
* @param targetType the target type, not necessarily the same as the method
|
||||
* parameter type, e.g. for {@code HttpEntity<String>}.
|
||||
* @param converterType the selected converter type
|
||||
* @return the value to use or {@code null} which may then raise an
|
||||
* {@code HttpMessageNotReadableException} if the argument is required.
|
||||
*/
|
||||
Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
|
||||
|
||||
/**
|
||||
* Invoked second before the request body is read and converted.
|
||||
* @param inputMessage the request
|
||||
* @param parameter the target method parameter
|
||||
* @param targetType the target type, not necessarily the same as the method
|
||||
* parameter type, e.g. for {@code HttpEntity<String>}.
|
||||
* @param converterType the converter used to deserialize the body
|
||||
* @return the input request or a new instance, never {@code null}
|
||||
*/
|
||||
HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
|
||||
|
||||
/**
|
||||
* Invoked third (and last) after the request body is converted to an Object.
|
||||
* @param body set to the converter Object before the 1st advice is called
|
||||
* @param inputMessage the request
|
||||
* @param parameter the target method parameter
|
||||
* @param targetType the target type, not necessarily the same as the method
|
||||
* parameter type, e.g. for {@code HttpEntity<String>}.
|
||||
* @param converterType the converter used to deserialize the body
|
||||
* @return the same body or a new instance
|
||||
*/
|
||||
Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
|
||||
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
|
||||
private List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
private List<Object> responseBodyAdvice = new ArrayList<Object>();
|
||||
private List<Object> requestResponseBodyAdvice = new ArrayList<Object>();
|
||||
|
||||
private WebBindingInitializer webBindingInitializer;
|
||||
|
||||
|
|
@ -329,15 +329,24 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
}
|
||||
|
||||
/**
|
||||
* Add one or more components to modify the response after the execution of a
|
||||
* controller method annotated with {@code @ResponseBody}, or a method returning
|
||||
* {@code ResponseEntity} and before the body is written to the response with
|
||||
* the selected {@code HttpMessageConverter}.
|
||||
* Add one or more {@code RequestBodyAdvice} instances to intercept the
|
||||
* request before it is read and converted for {@code @RequestBody} and
|
||||
* {@code HttpEntity} method arguments.
|
||||
*/
|
||||
public void setRequestBodyAdvice(List<RequestBodyAdvice> requestBodyAdvice) {
|
||||
if (requestBodyAdvice != null) {
|
||||
this.requestResponseBodyAdvice.addAll(requestBodyAdvice);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more {@code ResponseBodyAdvice} instances to intercept the
|
||||
* response before {@code @ResponseBody} or {@code ResponseEntity} return
|
||||
* values are written to the response body.
|
||||
*/
|
||||
public void setResponseBodyAdvice(List<ResponseBodyAdvice<?>> responseBodyAdvice) {
|
||||
this.responseBodyAdvice.clear();
|
||||
if (responseBodyAdvice != null) {
|
||||
this.responseBodyAdvice.addAll(responseBodyAdvice);
|
||||
this.requestResponseBodyAdvice.addAll(responseBodyAdvice);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -520,7 +529,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
|
||||
AnnotationAwareOrderComparator.sort(beans);
|
||||
|
||||
List<Object> responseBodyAdviceBeans = new ArrayList<Object>();
|
||||
List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();
|
||||
|
||||
for (ControllerAdviceBean bean : beans) {
|
||||
Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
|
||||
|
|
@ -533,14 +542,18 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
this.initBinderAdviceCache.put(bean, binderMethods);
|
||||
logger.info("Detected @InitBinder methods in " + bean);
|
||||
}
|
||||
if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
|
||||
requestResponseBodyAdviceBeans.add(bean);
|
||||
logger.info("Detected RequestBodyAdvice bean in " + bean);
|
||||
}
|
||||
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
|
||||
responseBodyAdviceBeans.add(bean);
|
||||
requestResponseBodyAdviceBeans.add(bean);
|
||||
logger.info("Detected ResponseBodyAdvice bean in " + bean);
|
||||
}
|
||||
}
|
||||
|
||||
if (!responseBodyAdviceBeans.isEmpty()) {
|
||||
this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
|
||||
if (!requestResponseBodyAdviceBeans.isEmpty()) {
|
||||
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -559,8 +572,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
resolvers.add(new MatrixVariableMethodArgumentResolver());
|
||||
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
|
||||
resolvers.add(new ServletModelAttributeMethodProcessor(false));
|
||||
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
|
||||
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
|
||||
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
|
||||
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
|
||||
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
|
||||
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
|
||||
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
|
||||
|
|
@ -569,7 +582,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
// Type-based argument resolution
|
||||
resolvers.add(new ServletRequestMethodArgumentResolver());
|
||||
resolvers.add(new ServletResponseMethodArgumentResolver());
|
||||
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
|
||||
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
|
||||
resolvers.add(new RedirectAttributesMethodArgumentResolver());
|
||||
resolvers.add(new ModelMethodProcessor());
|
||||
resolvers.add(new MapMethodProcessor());
|
||||
|
|
@ -633,8 +646,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
handlers.add(new ViewMethodReturnValueHandler());
|
||||
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
|
||||
handlers.add(new StreamingResponseBodyReturnValueHandler());
|
||||
handlers.add(new HttpEntityMethodProcessor(
|
||||
getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));
|
||||
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
|
||||
this.contentNegotiationManager, this.requestResponseBodyAdvice));
|
||||
handlers.add(new HttpHeadersReturnValueHandler());
|
||||
handlers.add(new CallableMethodReturnValueHandler());
|
||||
handlers.add(new DeferredResultMethodReturnValueHandler());
|
||||
|
|
@ -643,8 +656,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
|
||||
// Annotation-based return value types
|
||||
handlers.add(new ModelAttributeMethodProcessor(false));
|
||||
handlers.add(new RequestResponseBodyMethodProcessor(
|
||||
getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));
|
||||
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
|
||||
this.contentNegotiationManager, this.requestResponseBodyAdvice));
|
||||
|
||||
// Multi-purpose return value types
|
||||
handlers.add(new ViewNameMethodReturnValueHandler());
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
|
|
@ -74,10 +75,24 @@ import org.springframework.web.util.WebUtils;
|
|||
*/
|
||||
public class RequestPartMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* Basic constructor with converters only.
|
||||
*/
|
||||
public RequestPartMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
|
||||
super(messageConverters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with converters and {@code Request~} and
|
||||
* {@code ResponseBodyAdvice}.
|
||||
*/
|
||||
public RequestPartMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters,
|
||||
List<Object> requestResponseBodyAdvice) {
|
||||
|
||||
super(messageConverters, requestResponseBodyAdvice);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Supports the following:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.method.ControllerAdviceBean;
|
||||
|
||||
|
||||
/**
|
||||
* Invokes {@link RequestBodyAdvice} and {@link ResponseBodyAdvice} where each
|
||||
* instance may be (and is most likely) wrapped with
|
||||
* {@link org.springframework.web.method.ControllerAdviceBean ControllerAdviceBean}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.2
|
||||
*/
|
||||
class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyAdvice<Object> {
|
||||
|
||||
private final List<Object> requestBodyAdvice = new ArrayList<Object>(4);
|
||||
|
||||
private final List<Object> responseBodyAdvice = new ArrayList<Object>(4);
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance from a list of objects that are either of type
|
||||
* {@code ControllerAdviceBean} or {@code RequestBodyAdvice}.
|
||||
*/
|
||||
public RequestResponseBodyAdviceChain(List<Object> requestResponseBodyAdvice) {
|
||||
initAdvice(requestResponseBodyAdvice);
|
||||
}
|
||||
|
||||
private void initAdvice(List<Object> requestResponseBodyAdvice) {
|
||||
if (requestResponseBodyAdvice == null) {
|
||||
return;
|
||||
}
|
||||
for (Object advice : requestResponseBodyAdvice) {
|
||||
Class<?> beanType = (advice instanceof ControllerAdviceBean ?
|
||||
((ControllerAdviceBean) advice).getBeanType() : advice.getClass());
|
||||
if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
|
||||
this.requestBodyAdvice.add(advice);
|
||||
}
|
||||
else if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
|
||||
this.responseBodyAdvice.add(advice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Object> getAdvice(Class<?> adviceType) {
|
||||
if (RequestBodyAdvice.class.equals(adviceType)) {
|
||||
return this.requestBodyAdvice;
|
||||
}
|
||||
else if (ResponseBodyAdvice.class.equals(adviceType)) {
|
||||
return this.responseBodyAdvice;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unexpected adviceType: " + adviceType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter param, Type type, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
|
||||
if (advice.supports(parameter, targetType, converterType)) {
|
||||
body = advice.handleEmptyBody(body, inputMessage, parameter, targetType, converterType);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
|
||||
if (advice.supports(parameter, targetType, converterType)) {
|
||||
request = advice.beforeBodyRead(request, parameter, targetType, converterType);
|
||||
}
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
|
||||
if (advice.supports(parameter, targetType, converterType)) {
|
||||
body = advice.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType contentType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType,
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
return processBody(body, returnType, contentType, converterType, request, response);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Object processBody(Object body, MethodParameter returnType, MediaType contentType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType,
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
|
||||
if (advice.supports(returnType, converterType)) {
|
||||
body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
|
||||
contentType, converterType, request, response);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <A> List<A> getMatchingAdvice(MethodParameter parameter, Class<? extends A> adviceType) {
|
||||
List<Object> availableAdvice = getAdvice(adviceType);
|
||||
if (CollectionUtils.isEmpty(availableAdvice)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<A> result = new ArrayList<A>(availableAdvice.size());
|
||||
for (Object advice : availableAdvice) {
|
||||
if (advice instanceof ControllerAdviceBean) {
|
||||
ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
|
||||
if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) {
|
||||
continue;
|
||||
}
|
||||
advice = adviceBean.resolveBean();
|
||||
}
|
||||
if (adviceType.isAssignableFrom(advice.getClass())) {
|
||||
result.add((A) advice);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import java.io.InputStream;
|
|||
import java.io.PushbackInputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.core.Conventions;
|
||||
|
|
@ -59,20 +60,48 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
|
|||
*/
|
||||
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
|
||||
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
|
||||
super(messageConverters);
|
||||
|
||||
/**
|
||||
* Basic constructor with converters only. Suitable for resolving
|
||||
* {@code @RequestBody}. For handling {@code @ResponseBody} consider also
|
||||
* providing a {@code ContentNegotiationManager}.
|
||||
*/
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
|
||||
super(converters);
|
||||
}
|
||||
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager contentNegotiationManager) {
|
||||
/**
|
||||
* Basic constructor with converters and {@code ContentNegotiationManager}.
|
||||
* Suitable for resolving {@code @RequestBody} and handling
|
||||
* {@code @ResponseBody} without {@code Request~} or
|
||||
* {@code ResponseBodyAdvice}.
|
||||
*/
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
|
||||
ContentNegotiationManager manager) {
|
||||
|
||||
super(messageConverters, contentNegotiationManager);
|
||||
super(converters, manager);
|
||||
}
|
||||
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager contentNegotiationManager, List<Object> responseBodyAdvice) {
|
||||
/**
|
||||
* Complete constructor for resolving {@code @RequestBody} method arguments.
|
||||
* For handling {@code @ResponseBody} consider also providing a
|
||||
* {@code ContentNegotiationManager}.
|
||||
* @since 4.2
|
||||
*/
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
|
||||
List<Object> requestResponseBodyAdvice) {
|
||||
|
||||
super(messageConverters, contentNegotiationManager, responseBodyAdvice);
|
||||
super(converters, null, requestResponseBodyAdvice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete constructor for resolving {@code @RequestBody} and handling
|
||||
* {@code @ResponseBody}.
|
||||
*/
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
|
||||
ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
|
||||
|
||||
super(converters, manager, requestResponseBodyAdvice);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.method.ControllerAdviceBean;
|
||||
|
||||
/**
|
||||
* Invokes a a list of {@link ResponseBodyAdvice} beans.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
class ResponseBodyAdviceChain {
|
||||
|
||||
private final List<Object> advice;
|
||||
|
||||
|
||||
public ResponseBodyAdviceChain(List<Object> advice) {
|
||||
this.advice = advice;
|
||||
}
|
||||
|
||||
|
||||
public boolean hasAdvice() {
|
||||
return !CollectionUtils.isEmpty(this.advice);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T invoke(T body, MethodParameter returnType,
|
||||
MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (this.advice != null) {
|
||||
for (Object advice : this.advice) {
|
||||
if (advice instanceof ControllerAdviceBean) {
|
||||
ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
|
||||
if (!adviceBean.isApplicableToBeanType(returnType.getContainingClass())) {
|
||||
continue;
|
||||
}
|
||||
advice = adviceBean.resolveBean();
|
||||
}
|
||||
if (advice instanceof ResponseBodyAdvice) {
|
||||
ResponseBodyAdvice<T> typedAdvice = (ResponseBodyAdvice<T>) advice;
|
||||
if (typedAdvice.supports(returnType, selectedConverterType)) {
|
||||
body = typedAdvice.beforeBodyWrite(body, returnType,
|
||||
selectedContentType, selectedConverterType, request, response);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Expected ResponseBodyAdvice: " + advice);
|
||||
}
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
|||
loadBeanDefinitions("mvc-config-message-converters.xml");
|
||||
verifyMessageConverters(appContext.getBean(RequestMappingHandlerAdapter.class), true);
|
||||
verifyMessageConverters(appContext.getBean(ExceptionHandlerExceptionResolver.class), true);
|
||||
verifyResponseBodyAdvice(appContext.getBean(RequestMappingHandlerAdapter.class));
|
||||
verifyRequestResponseBodyAdvice(appContext.getBean(RequestMappingHandlerAdapter.class));
|
||||
verifyResponseBodyAdvice(appContext.getBean(ExceptionHandlerExceptionResolver.class));
|
||||
}
|
||||
|
||||
|
|
@ -182,6 +182,16 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
|||
assertTrue(converters.get(0) instanceof JsonViewResponseBodyAdvice);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void verifyRequestResponseBodyAdvice(Object bean) {
|
||||
assertNotNull(bean);
|
||||
Object value = new DirectFieldAccessor(bean).getPropertyValue("requestResponseBodyAdvice");
|
||||
assertNotNull(value);
|
||||
assertTrue(value instanceof List);
|
||||
List<ResponseBodyAdvice> converters = (List<ResponseBodyAdvice>) value;
|
||||
assertTrue(converters.get(0) instanceof JsonViewResponseBodyAdvice);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestWebArgumentResolver implements WebArgumentResolver {
|
||||
|
|
|
|||
|
|
@ -188,7 +188,8 @@ public class WebMvcConfigurationSupportTests {
|
|||
assertTrue(validator instanceof LocalValidatorFactoryBean);
|
||||
|
||||
DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(adapter);
|
||||
List<Object> interceptors = (List<Object>) fieldAccessor.getPropertyValue("responseBodyAdvice");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> interceptors = (List<Object>) fieldAccessor.getPropertyValue("requestResponseBodyAdvice");
|
||||
assertEquals(1, interceptors.size());
|
||||
assertEquals(JsonViewResponseBodyAdvice.class, interceptors.get(0).getClass());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -16,13 +16,18 @@
|
|||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
|
|
@ -38,17 +43,13 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
|
|||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.method.ControllerAdviceBean;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
/**
|
||||
* Unit tests for
|
||||
* {@link ResponseBodyAdviceChain}.
|
||||
* Unit tests for {@link RequestResponseBodyAdviceChain}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
* @since 4.2
|
||||
*/
|
||||
public class ResponseBodyAdviceChainTests {
|
||||
public class RequestResponseBodyAdviceChainTests {
|
||||
|
||||
private String body;
|
||||
|
||||
|
|
@ -56,10 +57,10 @@ public class ResponseBodyAdviceChainTests {
|
|||
|
||||
private Class<? extends HttpMessageConverter<?>> converterType;
|
||||
|
||||
private MethodParameter paramType;
|
||||
private MethodParameter returnType;
|
||||
|
||||
private ServerHttpRequest request;
|
||||
|
||||
private ServerHttpResponse response;
|
||||
|
||||
|
||||
|
|
@ -68,25 +69,53 @@ public class ResponseBodyAdviceChainTests {
|
|||
this.body = "body";
|
||||
this.contentType = MediaType.TEXT_PLAIN;
|
||||
this.converterType = StringHttpMessageConverter.class;
|
||||
this.returnType = new MethodParameter(ClassUtils.getMethod(this.getClass(), "handle"), -1);
|
||||
this.paramType = new MethodParameter(ClassUtils.getMethod(this.getClass(), "handle", String.class), 0);
|
||||
this.returnType = new MethodParameter(ClassUtils.getMethod(this.getClass(), "handle", String.class), -1);
|
||||
this.request = new ServletServerHttpRequest(new MockHttpServletRequest());
|
||||
this.response = new ServletServerHttpResponse(new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void requestBodyAdvice() {
|
||||
|
||||
RequestBodyAdvice requestAdvice = Mockito.mock(RequestBodyAdvice.class);
|
||||
ResponseBodyAdvice<String> responseAdvice = Mockito.mock(ResponseBodyAdvice.class);
|
||||
List<Object> advice = Arrays.asList(requestAdvice, responseAdvice);
|
||||
RequestResponseBodyAdviceChain chain = new RequestResponseBodyAdviceChain(advice);
|
||||
|
||||
HttpInputMessage wrapped = new ServletServerHttpRequest(new MockHttpServletRequest());
|
||||
given(requestAdvice.supports(this.paramType, String.class, this.converterType)).willReturn(true);
|
||||
given(requestAdvice.beforeBodyRead(eq(this.request), eq(this.paramType), eq(String.class),
|
||||
eq(this.converterType))).willReturn(wrapped);
|
||||
|
||||
assertSame(wrapped, chain.beforeBodyRead(this.request, this.paramType, String.class, this.converterType));
|
||||
|
||||
String modified = "body++";
|
||||
given(requestAdvice.afterBodyRead(eq(this.body), eq(this.request), eq(this.paramType),
|
||||
eq(String.class), eq(this.converterType))).willReturn(modified);
|
||||
|
||||
assertEquals(modified, chain.afterBodyRead(this.body, this.request, this.paramType,
|
||||
String.class, this.converterType));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void responseBodyAdvice() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ResponseBodyAdvice<String> advice = Mockito.mock(ResponseBodyAdvice.class);
|
||||
ResponseBodyAdviceChain chain = new ResponseBodyAdviceChain(Arrays.asList(advice));
|
||||
RequestBodyAdvice requestAdvice = Mockito.mock(RequestBodyAdvice.class);
|
||||
ResponseBodyAdvice<String> responseAdvice = Mockito.mock(ResponseBodyAdvice.class);
|
||||
List<Object> advice = Arrays.asList(requestAdvice, responseAdvice);
|
||||
RequestResponseBodyAdviceChain chain = new RequestResponseBodyAdviceChain(advice);
|
||||
|
||||
String expected = "body++";
|
||||
given(advice.supports(this.returnType, this.converterType)).willReturn(true);
|
||||
given(advice.beforeBodyWrite(eq(this.body), eq(this.returnType), eq(this.contentType),
|
||||
given(responseAdvice.supports(this.returnType, this.converterType)).willReturn(true);
|
||||
given(responseAdvice.beforeBodyWrite(eq(this.body), eq(this.returnType), eq(this.contentType),
|
||||
eq(this.converterType), same(this.request), same(this.response))).willReturn(expected);
|
||||
|
||||
String actual = chain.invoke(this.body, this.returnType,
|
||||
this.contentType, this.converterType, this.request, this.response);
|
||||
String actual = (String) chain.beforeBodyWrite(this.body, this.returnType, this.contentType,
|
||||
this.converterType, this.request, this.response);
|
||||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
|
@ -95,10 +124,10 @@ public class ResponseBodyAdviceChainTests {
|
|||
public void controllerAdvice() {
|
||||
|
||||
Object adviceBean = new ControllerAdviceBean(new MyControllerAdvice());
|
||||
ResponseBodyAdviceChain chain = new ResponseBodyAdviceChain(Arrays.asList(adviceBean));
|
||||
RequestResponseBodyAdviceChain chain = new RequestResponseBodyAdviceChain(Arrays.asList(adviceBean));
|
||||
|
||||
String actual = chain.invoke(this.body, this.returnType,
|
||||
this.contentType, this.converterType, this.request, this.response);
|
||||
String actual = (String) chain.beforeBodyWrite(this.body, this.returnType, this.contentType,
|
||||
this.converterType, this.request, this.response);
|
||||
|
||||
assertEquals("body-MyControllerAdvice", actual);
|
||||
}
|
||||
|
|
@ -107,10 +136,10 @@ public class ResponseBodyAdviceChainTests {
|
|||
public void controllerAdviceNotApplicable() {
|
||||
|
||||
Object adviceBean = new ControllerAdviceBean(new TargetedControllerAdvice());
|
||||
ResponseBodyAdviceChain chain = new ResponseBodyAdviceChain(Arrays.asList(adviceBean));
|
||||
RequestResponseBodyAdviceChain chain = new RequestResponseBodyAdviceChain(Arrays.asList(adviceBean));
|
||||
|
||||
String actual = chain.invoke(this.body, this.returnType,
|
||||
this.contentType, this.converterType, this.request, this.response);
|
||||
String actual = (String) chain.beforeBodyWrite(this.body, this.returnType, this.contentType,
|
||||
this.converterType, this.request, this.response);
|
||||
|
||||
assertEquals(this.body, actual);
|
||||
}
|
||||
|
|
@ -152,10 +181,10 @@ public class ResponseBodyAdviceChainTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ResponseBody
|
||||
public String handle() {
|
||||
public String handle(String body) {
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue