Add ResponseBodyInterceptor
This change introduces a new ResponseBodyInterceptor interface that can be used to modify the response after @ResponseBody or ResponseEntity methods but before the body is actually written to the response with the selected HttpMessageConverter. The RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver each have a property to configure such interceptors. In addition both RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver detect if any @ControllerAdvice bean implements ResponseBodyInterceptor and use it accordingly. Issue: SPR-10859
This commit is contained in:
parent
f73a8baec7
commit
96b18c8dc2
|
|
@ -55,6 +55,8 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
|||
|
||||
private final ContentNegotiationManager contentNegotiationManager;
|
||||
|
||||
private final ResponseBodyInterceptorChain interceptorChain;
|
||||
|
||||
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this(messageConverters, null);
|
||||
|
|
@ -62,9 +64,15 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
|||
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager manager) {
|
||||
this(messageConverters, manager, null);
|
||||
}
|
||||
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager manager, List<Object> responseBodyInterceptors) {
|
||||
|
||||
super(messageConverters);
|
||||
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
|
||||
this.interceptorChain = new ResponseBodyInterceptorChain(responseBodyInterceptors);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -152,6 +160,9 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
|||
}
|
||||
}
|
||||
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
|
||||
returnValue = this.interceptorChain.invoke(returnValue, selectedMediaType,
|
||||
(Class<HttpMessageConverter<T>>) messageConverter.getClass(),
|
||||
returnType, inputMessage, outputMessage);
|
||||
((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
|
||||
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
|
||||
|
||||
private final List<Object> responseBodyInterceptors = new ArrayList<Object>();
|
||||
|
||||
|
||||
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
|
||||
new ConcurrentHashMap<Class<?>, ExceptionHandlerMethodResolver>(64);
|
||||
|
||||
|
|
@ -106,6 +109,19 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more interceptors to be invoked after the execution of a controller
|
||||
* method annotated with {@code @ResponseBody} or returning {@code ResponseEntity}
|
||||
* but before the body is written to the response with the selected
|
||||
* {@code HttpMessageConverter}.
|
||||
*/
|
||||
public void setResponseBodyInterceptors(List<ResponseBodyInterceptor> responseBodyInterceptors) {
|
||||
this.responseBodyInterceptors.clear();
|
||||
if (responseBodyInterceptors != null) {
|
||||
this.responseBodyInterceptors.addAll(responseBodyInterceptors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide resolvers for custom argument types. Custom resolvers are ordered
|
||||
* after built-in ones. To override the built-in support for argument
|
||||
|
|
@ -233,6 +249,10 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
// Do this first, it may add ResponseBody interceptors
|
||||
initExceptionHandlerAdviceCache();
|
||||
|
||||
if (this.argumentResolvers == null) {
|
||||
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
|
||||
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
|
||||
|
|
@ -241,7 +261,30 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
|
||||
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
|
||||
}
|
||||
initExceptionHandlerAdviceCache();
|
||||
}
|
||||
|
||||
private void initExceptionHandlerAdviceCache() {
|
||||
if (getApplicationContext() == null) {
|
||||
return;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for exception mappings: " + getApplicationContext());
|
||||
}
|
||||
|
||||
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
|
||||
Collections.sort(beans, new OrderComparator());
|
||||
|
||||
for (ControllerAdviceBean bean : beans) {
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(bean.getBeanType());
|
||||
if (resolver.hasExceptionMappings()) {
|
||||
this.exceptionHandlerAdviceCache.put(bean, resolver);
|
||||
logger.info("Detected @ExceptionHandler methods in " + bean);
|
||||
}
|
||||
if (ResponseBodyInterceptor.class.isAssignableFrom(bean.getBeanType())) {
|
||||
this.responseBodyInterceptors.add(bean);
|
||||
logger.info("Detected ResponseBodyInterceptor implementation in " + bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -274,11 +317,13 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
handlers.add(new ModelAndViewMethodReturnValueHandler());
|
||||
handlers.add(new ModelMethodProcessor());
|
||||
handlers.add(new ViewMethodReturnValueHandler());
|
||||
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
|
||||
handlers.add(new HttpEntityMethodProcessor(
|
||||
getMessageConverters(), this.contentNegotiationManager, this.responseBodyInterceptors));
|
||||
|
||||
// Annotation-based return value types
|
||||
handlers.add(new ModelAttributeMethodProcessor(false));
|
||||
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
|
||||
handlers.add(new RequestResponseBodyMethodProcessor(
|
||||
getMessageConverters(), this.contentNegotiationManager, this.responseBodyInterceptors));
|
||||
|
||||
// Multi-purpose return value types
|
||||
handlers.add(new ViewNameMethodReturnValueHandler());
|
||||
|
|
@ -295,26 +340,6 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
return handlers;
|
||||
}
|
||||
|
||||
private void initExceptionHandlerAdviceCache() {
|
||||
if (getApplicationContext() == null) {
|
||||
return;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for exception mappings: " + getApplicationContext());
|
||||
}
|
||||
|
||||
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
|
||||
Collections.sort(beans, new OrderComparator());
|
||||
|
||||
for (ControllerAdviceBean bean : beans) {
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(bean.getBeanType());
|
||||
if (resolver.hasExceptionMappings()) {
|
||||
this.exceptionHandlerAdviceCache.put(bean, resolver);
|
||||
logger.info("Detected @ExceptionHandler methods in " + bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an {@code @ExceptionHandler} method and invoke it to handle the raised exception.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -57,12 +57,16 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager contentNegotiationManager) {
|
||||
|
||||
super(messageConverters, contentNegotiationManager);
|
||||
}
|
||||
|
||||
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager contentNegotiationManager, List<Object> responseBodyInterceptors) {
|
||||
super(messageConverters, contentNegotiationManager, responseBodyInterceptors);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return HttpEntity.class.equals(parameter.getParameterType());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,8 +132,11 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
|
||||
private List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
private List<Object> responseBodyInterceptors = new ArrayList<Object>();
|
||||
|
||||
private WebBindingInitializer webBindingInitializer;
|
||||
|
||||
|
||||
private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");
|
||||
|
||||
private Long asyncRequestTimeout;
|
||||
|
|
@ -306,6 +309,14 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
return modelAndViewResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ContentNegotiationManager} to use to determine requested media types.
|
||||
* If not set, the default constructor is used.
|
||||
*/
|
||||
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
|
||||
this.contentNegotiationManager = contentNegotiationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the converters to use in argument resolvers and return value
|
||||
* handlers that support reading and/or writing to the body of the
|
||||
|
|
@ -315,14 +326,6 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ContentNegotiationManager} to use to determine requested media types.
|
||||
* If not set, the default constructor is used.
|
||||
*/
|
||||
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
|
||||
this.contentNegotiationManager = contentNegotiationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured message body converters.
|
||||
*/
|
||||
|
|
@ -330,6 +333,19 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
return messageConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more interceptors to be invoked after the execution of a controller
|
||||
* method annotated with {@code @ResponseBody} or returning {@code ResponseEntity}
|
||||
* but before the body is written to the response with the selected
|
||||
* {@code HttpMessageConverter}.
|
||||
*/
|
||||
public void setResponseBodyInterceptors(List<ResponseBodyInterceptor> responseBodyInterceptors) {
|
||||
this.responseBodyInterceptors.clear();
|
||||
if (responseBodyInterceptors != null) {
|
||||
this.responseBodyInterceptors.addAll(responseBodyInterceptors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a WebBindingInitializer with "global" initialization to apply
|
||||
* to every DataBinder instance.
|
||||
|
|
@ -481,6 +497,10 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
// Do this first, it may add ResponseBody interceptors
|
||||
initControllerAdviceCache();
|
||||
|
||||
if (this.argumentResolvers == null) {
|
||||
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
|
||||
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
|
||||
|
|
@ -493,7 +513,35 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
|
||||
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
|
||||
}
|
||||
initControllerAdviceCache();
|
||||
}
|
||||
|
||||
private void initControllerAdviceCache() {
|
||||
if (getApplicationContext() == null) {
|
||||
return;
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
|
||||
}
|
||||
|
||||
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
|
||||
Collections.sort(beans, new OrderComparator());
|
||||
|
||||
for (ControllerAdviceBean bean : beans) {
|
||||
Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
|
||||
if (!attrMethods.isEmpty()) {
|
||||
this.modelAttributeAdviceCache.put(bean, attrMethods);
|
||||
logger.info("Detected @ModelAttribute methods in " + bean);
|
||||
}
|
||||
Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
|
||||
if (!binderMethods.isEmpty()) {
|
||||
this.initBinderAdviceCache.put(bean, binderMethods);
|
||||
logger.info("Detected @InitBinder methods in " + bean);
|
||||
}
|
||||
if (ResponseBodyInterceptor.class.isAssignableFrom(bean.getBeanType())) {
|
||||
this.responseBodyInterceptors.add(bean);
|
||||
logger.info("Detected ResponseBodyInterceptor implementation in " + bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -583,7 +631,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
handlers.add(new ModelAndViewMethodReturnValueHandler());
|
||||
handlers.add(new ModelMethodProcessor());
|
||||
handlers.add(new ViewMethodReturnValueHandler());
|
||||
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
|
||||
handlers.add(new HttpEntityMethodProcessor(
|
||||
getMessageConverters(), this.contentNegotiationManager, this.responseBodyInterceptors));
|
||||
handlers.add(new HttpHeadersReturnValueHandler());
|
||||
handlers.add(new CallableMethodReturnValueHandler());
|
||||
handlers.add(new DeferredResultMethodReturnValueHandler());
|
||||
|
|
@ -592,7 +641,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
|
||||
// Annotation-based return value types
|
||||
handlers.add(new ModelAttributeMethodProcessor(false));
|
||||
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
|
||||
handlers.add(new RequestResponseBodyMethodProcessor(
|
||||
getMessageConverters(), this.contentNegotiationManager, this.responseBodyInterceptors));
|
||||
|
||||
// Multi-purpose return value types
|
||||
handlers.add(new ViewNameMethodReturnValueHandler());
|
||||
|
|
@ -614,31 +664,6 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
|||
return handlers;
|
||||
}
|
||||
|
||||
private void initControllerAdviceCache() {
|
||||
if (getApplicationContext() == null) {
|
||||
return;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for controller advice: " + getApplicationContext());
|
||||
}
|
||||
|
||||
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
|
||||
Collections.sort(beans, new OrderComparator());
|
||||
|
||||
for (ControllerAdviceBean bean : beans) {
|
||||
Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
|
||||
if (!attrMethods.isEmpty()) {
|
||||
this.modelAttributeAdviceCache.put(bean, attrMethods);
|
||||
logger.info("Detected @ModelAttribute methods in " + bean);
|
||||
}
|
||||
Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
|
||||
if (!binderMethods.isEmpty()) {
|
||||
this.initBinderAdviceCache.put(bean, binderMethods);
|
||||
logger.info("Detected @InitBinder methods in " + bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always return {@code true} since any method argument and return value
|
||||
* type will be processed in some way. A method argument not recognized
|
||||
|
|
|
|||
|
|
@ -69,10 +69,15 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
|||
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager contentNegotiationManager) {
|
||||
|
||||
super(messageConverters, contentNegotiationManager);
|
||||
}
|
||||
|
||||
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
|
||||
ContentNegotiationManager contentNegotiationManager, List<Object> responseBodyInterceptors) {
|
||||
super(messageConverters, contentNegotiationManager, responseBodyInterceptors);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestBody.class);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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 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;
|
||||
|
||||
/**
|
||||
* Allows customizing the response after the execution of an {@code @ResponseBody}
|
||||
* or an {@code ResponseEntity} controller method but before the body is written
|
||||
* with an {@code HttpMessageConverter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface ResponseBodyInterceptor {
|
||||
|
||||
/**
|
||||
* Invoked after an {@code HttpMessageConverter} is selected and just before
|
||||
* its write method is invoked.
|
||||
*
|
||||
* @param body the body to be written
|
||||
* @param contentType the selected content type
|
||||
* @param converterType the selected converter that will write the body
|
||||
* @param returnType the return type of the controller method
|
||||
* @param request the current request
|
||||
* @param response the current response
|
||||
* @param <T> the type supported by the message converter
|
||||
*
|
||||
* @return the body that was passed in or a modified, possibly new instance
|
||||
*/
|
||||
<T> T beforeBodyWrite(T body, MediaType contentType, Class<HttpMessageConverter<T>> converterType,
|
||||
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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 org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
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.Assert;
|
||||
import org.springframework.web.method.ControllerAdviceBean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Invokes a a list of ResponseBodyInterceptor's.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
class ResponseBodyInterceptorChain {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ResponseBodyInterceptorChain.class);
|
||||
|
||||
private final List<Object> interceptors;
|
||||
|
||||
|
||||
public ResponseBodyInterceptorChain(List<Object> interceptors) {
|
||||
this.interceptors = interceptors;
|
||||
}
|
||||
|
||||
|
||||
public <T> T invoke(T body, MediaType contentType, Class<HttpMessageConverter<T>> converterType,
|
||||
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (this.interceptors != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking ResponseBody interceptor chain for body=" + body);
|
||||
}
|
||||
for (Object interceptor : this.interceptors) {
|
||||
if (interceptor instanceof ControllerAdviceBean) {
|
||||
ControllerAdviceBean adviceBean = (ControllerAdviceBean) interceptor;
|
||||
if (!adviceBean.isApplicableToBeanType(returnType.getContainingClass())) {
|
||||
continue;
|
||||
}
|
||||
interceptor = adviceBean.resolveBean();
|
||||
}
|
||||
Assert.state(interceptor instanceof ResponseBodyInterceptor);
|
||||
body = ((ResponseBodyInterceptor) interceptor).beforeBodyWrite(
|
||||
body, contentType, converterType, returnType, request, response);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("After interceptor chain body=" + body);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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;
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
public class ResponseBodyInterceptorChainTests {
|
||||
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue