Spring 的 ResponseBodyAdvice:执行顺序?
Posted
技术标签:
【中文标题】Spring 的 ResponseBodyAdvice:执行顺序?【英文标题】:Spring's ResponseBodyAdvice: Order of execution? 【发布时间】:2015-04-15 05:19:29 【问题描述】:我想实现一个自定义ResponseBodyAdvice
,它只是查找Page<?>
,然后将总元素数添加到响应标头中。
@ControllerAdvice
public class PageResponseAdvice implements ResponseBodyAdvice<Object>
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType)
return Page.class.isAssignableFrom(returnType.getParameterType());
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response)
((Page<?>) body).getTotalElements();
...
问题是:
为什么body
(在beforeBodyWrite
方法中)是MappingJacksonValue
类型的?
有没有更好的方法来实现这一点? / 我是否使用了错误的拦截器?
我不想处理包装类,尽管我只想要 beforeBodyWrite
方法中的普通未修改的 Page
对象。
编辑:
我现在只是extend AbstractMappingJacksonResponseBodyAdvice
。这很好用,但 感觉 不对。也许有人还有更好的主意。
这是Page -> Content-Range Header
的代码:
@ControllerAdvice
public class PageResponseAdvice extends AbstractMappingJacksonResponseBodyAdvice
@Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType)
return super.supports(returnType, converterType) && Page.class.isAssignableFrom(returnType.getParameterType());
@Override protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response)
Page<?> page = ((Page<?>) bodyContainer.getValue());
Long from = null;
Long to = null;
if(page.getTotalElements() > 0 && page.getNumberOfElements() > 0)
from = Integer.valueOf(page.getNumber()).longValue()*page.getSize();
to = from + page.getNumberOfElements() - 1;
response.getHeaders().add(
HttpHeaders.CONTENT_RANGE,
ContentRangeEncoder.encode(
"items",
from,
to,
page.getTotalElements()
)
);
response.getHeaders().add(
HttpHeaders.ACCEPT_RANGES,
"items"
);
(如果有人感兴趣。这里是ContentRangeEncoder
):
public class ContentRangeEncoder
private static final Pattern TYPE_PATTERN = Pattern.compile("[a-zA-Z0-9]+");
private static final Predicate<String> TYPE_PATTERN_PREDICATE = TYPE_PATTERN.asPredicate();
public static <T extends Number & Comparable<T>> String encode(String unit, T from, T to, T length)
StringBuilder sb = new StringBuilder();
if(unit != null)
Assert.isTrue(TYPE_PATTERN_PREDICATE.test(unit));
sb.append(unit).append(" ");
if(from == null && to == null)
sb.append("*");
else
Assert.notNull(from);
Assert.notNull(to);
Assert.isTrue(from.compareTo(to) <= 0);
sb.append(from).append("-").append(to);
sb.append("/");
if(length == null)
sb.append("*");
else
Assert.isTrue(to == null || length.compareTo(to) > 0);
sb.append(length);
return sb.toString();
【问题讨论】:
【参考方案1】:至于为什么 body 是 MappingJacksonValue
类型,这取决于你的配置和你的特定处理程序方法的返回值。
作为旁注,WebMvcConfigurationSupport
将JsonViewResponseBodyAdvice
添加到RequestMappingHandlerAdapter
,如果这个ResponseBodyAdvice
bean 在你之前被调用,这就解释了为什么body 是MappingJacksonValue
。主体从一个 ResponseBodyAdvice
bean 传递到下一个,每个都以它认为合适的任何“形式”返回主体。
这是JsonViewResponseBodyAdvice
的超类“AbstractMappingJacksonResponseBodyAdvice”的摘录,看看身体会发生什么:
public final Object beforeBodyWrite(@Nullable Object body,
MethodParameter returnType, ..., ..., ..., ...)
//...
MappingJacksonValue container = getOrCreateContainer(body);
//...
return container;
【讨论】:
以上是关于Spring 的 ResponseBodyAdvice:执行顺序?的主要内容,如果未能解决你的问题,请参考以下文章
学习笔记——Spring简介;Spring搭建步骤;Spring的特性;Spring中getBean三种方式;Spring中的标签