Parameterize ResponseBodyInterceptor on the type level
Issue: SPR-10859
This commit is contained in:
@@ -425,8 +425,9 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
adapter.setCustomReturnValueHandlers(returnValueHandlers);
|
||||
|
||||
if (jackson2Present) {
|
||||
ResponseBodyInterceptor interceptor = new JsonViewResponseBodyInterceptor();
|
||||
adapter.setResponseBodyInterceptors(Arrays.asList(interceptor));
|
||||
List<ResponseBodyInterceptor<?>> interceptors = new ArrayList<ResponseBodyInterceptor<?>>();
|
||||
interceptors.add(new JsonViewResponseBodyInterceptor());
|
||||
adapter.setResponseBodyInterceptors(interceptors);
|
||||
}
|
||||
|
||||
AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
|
||||
@@ -711,8 +712,9 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
exceptionHandlerExceptionResolver.setContentNegotiationManager(mvcContentNegotiationManager());
|
||||
exceptionHandlerExceptionResolver.setMessageConverters(getMessageConverters());
|
||||
if (jackson2Present) {
|
||||
ResponseBodyInterceptor interceptor = new JsonViewResponseBodyInterceptor();
|
||||
exceptionHandlerExceptionResolver.setResponseBodyInterceptors(Arrays.asList(interceptor));
|
||||
List<ResponseBodyInterceptor<?>> interceptors = new ArrayList<ResponseBodyInterceptor<?>>();
|
||||
interceptors.add(new JsonViewResponseBodyInterceptor());
|
||||
exceptionHandlerExceptionResolver.setResponseBodyInterceptors(interceptors);
|
||||
}
|
||||
exceptionHandlerExceptionResolver.afterPropertiesSet();
|
||||
|
||||
|
||||
@@ -33,25 +33,27 @@ import org.springframework.http.server.ServerHttpResponse;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class AbstractMappingJacksonResponseBodyInterceptor implements ResponseBodyInterceptor {
|
||||
public abstract class AbstractMappingJacksonResponseBodyInterceptor
|
||||
implements ResponseBodyInterceptor<Object> {
|
||||
|
||||
|
||||
protected AbstractMappingJacksonResponseBodyInterceptor() {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final <T> T beforeBodyWrite(T body, MediaType contentType,
|
||||
Class<? extends HttpMessageConverter<T>> converterType,
|
||||
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return MappingJackson2HttpMessageConverter.class.equals(converterType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object beforeBodyWrite(Object body, MethodParameter returnType,
|
||||
MediaType contentType, Class<? extends HttpMessageConverter<?>> converterType,
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (!MappingJackson2HttpMessageConverter.class.equals(converterType)) {
|
||||
return body;
|
||||
}
|
||||
MappingJacksonValue container = getOrCreateContainer(body);
|
||||
beforeBodyWriteInternal(container, contentType, returnType, request, response);
|
||||
return (T) container;
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
@@ -148,9 +149,8 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||
selectedMediaType = selectedMediaType.removeQualityValue();
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
|
||||
returnValue = this.interceptorChain.invoke(returnValue, selectedMediaType,
|
||||
(Class<HttpMessageConverter<T>>) messageConverter.getClass(),
|
||||
returnType, inputMessage, outputMessage);
|
||||
returnValue = this.interceptorChain.invoke(returnValue, returnType, selectedMediaType,
|
||||
(Class<HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
|
||||
((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
|
||||
|
||||
@@ -115,7 +115,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||
* but before the body is written to the response with the selected
|
||||
* {@code HttpMessageConverter}.
|
||||
*/
|
||||
public void setResponseBodyInterceptors(List<ResponseBodyInterceptor> responseBodyInterceptors) {
|
||||
public void setResponseBodyInterceptors(List<ResponseBodyInterceptor<?>> responseBodyInterceptors) {
|
||||
this.responseBodyInterceptors.clear();
|
||||
if (responseBodyInterceptors != null) {
|
||||
this.responseBodyInterceptors.addAll(responseBodyInterceptors);
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonValue;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
@@ -40,18 +41,17 @@ import org.springframework.util.Assert;
|
||||
public class JsonViewResponseBodyInterceptor extends AbstractMappingJacksonResponseBodyInterceptor {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return (super.supports(returnType, converterType) && returnType.getMethodAnnotation(JsonView.class) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
|
||||
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
JsonView annotation = returnType.getMethodAnnotation(JsonView.class);
|
||||
if (annotation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.isTrue(annotation.value().length != 0,
|
||||
"Expected at least one serialization view class in JsonView annotation on " + returnType);
|
||||
|
||||
Assert.isTrue(annotation.value().length != 0, "No view class in JsonView annotation on " + returnType);
|
||||
bodyContainer.setSerializationView(annotation.value()[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -339,7 +339,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
||||
* but before the body is written to the response with the selected
|
||||
* {@code HttpMessageConverter}.
|
||||
*/
|
||||
public void setResponseBodyInterceptors(List<ResponseBodyInterceptor> responseBodyInterceptors) {
|
||||
public void setResponseBodyInterceptors(List<ResponseBodyInterceptor<?>> responseBodyInterceptors) {
|
||||
this.responseBodyInterceptors.clear();
|
||||
if (responseBodyInterceptors != null) {
|
||||
this.responseBodyInterceptors.addAll(responseBodyInterceptors);
|
||||
|
||||
@@ -30,23 +30,33 @@ import org.springframework.http.server.ServerHttpResponse;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface ResponseBodyInterceptor {
|
||||
public interface ResponseBodyInterceptor<T> {
|
||||
|
||||
/**
|
||||
* Whether this interceptor supports the given controller method return type
|
||||
* and the selected {@code HttpMessageConverter} type.
|
||||
*
|
||||
* @param returnType the return type
|
||||
* @param converterType the selected converter type
|
||||
* @return {@code true} if beforeBodyWrite should be invoked, {@code false} otherwise
|
||||
*/
|
||||
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
|
||||
|
||||
/**
|
||||
* 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 selectedContentType the content type selected through content negotiation
|
||||
* @param selectedConverterType the converter type selected to write to the response
|
||||
* @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<? extends HttpMessageConverter<T>> converterType,
|
||||
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response);
|
||||
T beforeBodyWrite(T body, MethodParameter returnType,
|
||||
MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
|
||||
ServerHttpRequest request, ServerHttpResponse response);
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ 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;
|
||||
@@ -46,8 +45,10 @@ class ResponseBodyInterceptorChain {
|
||||
}
|
||||
|
||||
|
||||
public <T> T invoke(T body, MediaType contentType, Class<? extends HttpMessageConverter<T>> converterType,
|
||||
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T invoke(T body, MethodParameter returnType,
|
||||
MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (this.interceptors != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -61,9 +62,16 @@ class ResponseBodyInterceptorChain {
|
||||
}
|
||||
interceptor = adviceBean.resolveBean();
|
||||
}
|
||||
Assert.state(interceptor instanceof ResponseBodyInterceptor);
|
||||
body = ((ResponseBodyInterceptor) interceptor).beforeBodyWrite(
|
||||
body, contentType, converterType, returnType, request, response);
|
||||
if (interceptor instanceof ResponseBodyInterceptor) {
|
||||
ResponseBodyInterceptor<T> typedInterceptor = (ResponseBodyInterceptor<T>) interceptor;
|
||||
if (typedInterceptor.supports(returnType, selectedConverterType)) {
|
||||
body = typedInterceptor.beforeBodyWrite(body, returnType,
|
||||
selectedContentType, selectedConverterType, request, response);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Expected a ResponseBodyInterceptor: " + interceptor);
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("After interceptor chain body=" + body);
|
||||
|
||||
Reference in New Issue
Block a user