我在REST控制器中使用Spring数据分页并返回Paged实体。我想在JSONViews的帮助下控制作为JSON返回的数据。
当我返回单个对象时,我能够实现结果。但是当我返回Page时,我收到空白的JSON作为回复。
以下是我的方法签名。
@JsonView(TravelRequestView.MyRequests.class)
@RequestMapping("/travel/requests")
public Page<TravelRequest> getUserTravelRequests(
@RequestParam("ps") int pageSize, @RequestParam("p") int page,
@RequestParam(defaultValue = "", value = "q") String searchString)
当我删除@JsonView注释时,我能够收到响应。
答案 0 :(得分:7)
设置DEFAULT_VIEW_INCLUSION具有全局效果,而我们所需要的只是能够序列化Page对象。以下代码将为Page注册序列化程序,并且只是对代码的简单更改:
@Bean
public Module springDataPageModule() {
return new SimpleModule().addSerializer(Page.class, new JsonSerializer<Page>() {
@Override
public void serialize(Page value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeNumberField("totalElements",value.getTotalElements());
gen.writeNumberField("totalPages", value.getTotalPages());
gen.writeNumberField("number", value.getNumber());
gen.writeNumberField("size", value.getSize());
gen.writeBooleanField("first", value.isFirst());
gen.writeBooleanField("last", value.isLast());
gen.writeFieldName("content");
serializers.defaultSerializeValue(value.getContent(),gen);
gen.writeEndObject();
}
});
}
另一个(可以说是更优雅的)解决方案是注册以下ResponseBodyAdvice。它将确保您的REST端点仍将返回JSON数组,并设置HTTP标头“X-Has-Next-Page”以指示是否有更多数据。优点是: 1)没有额外的计数(*)查询到您的数据库(单个查询) 2)响应更优雅,因为它返回一个JSON数组
/**
* ResponseBodyAdvice to support Spring data Slice object in JSON responses.
* If the value is a slice, we'll write the List as an array, and add a header to the HTTP response
*
* @author blagerweij
*/
@ControllerAdvice
public class SliceResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Slice) {
Slice slice = (Slice) body;
response.getHeaders().add("X-Has-Next-Page", String.valueOf(slice.hasNext()));
return slice.getContent();
}
return body;
}
}
答案 1 :(得分:7)
如果您使用的是spring-boot,那么另一个更简单的解决方案是将以下内容添加到application.yml
spring:
jackson:
mapper:
DEFAULT_VIEW_INCLUSION: true
或application.properties
spring.jackson.mapper.DEFAULT_VIEW_INCLUSION=true
使用这种方法,我们的优势在于保留了Spring容器管理的ObjectMapper
而不创建新的ObjectMapper。因此,一旦我们使用spring管理的ObjectMapper,我们定义的任何Custom Serialzers仍将继续工作,例如CustomDateSerialzer
参考:http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html
答案 2 :(得分:4)
尝试下面的代码,
@Configuration
public class MyInterceptorConfig extends WebMvcConfigurerAdapter{
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper() {
private static final long serialVersionUID = 1L;
@Override
protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) {
// replace the configuration with my modified configuration.
// calling "withView" should keep previous config and just add my changes.
return super._serializerProvider(config.withView(TravelRequestView.MyRequests.class));
}
};
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
converter.setObjectMapper(mapper);
converters.add(converter);
}
虽然我不想因此而受到赞扬, 这是
的参考Jackson JsonView not being applied
它将检索用jsonview(TravelRequestView.MyRequests.class)注释的实体的所有变量以及所有未使用jsonview注释的变量。如果您不想要某个对象的某些属性,请使用不同的视图进行注释。
答案 3 :(得分:2)
我实际上找到了一种更简单,更好的方法。我遇到的问题是我无法在我收到的Page对象上设置@JsonView注释。所以我创建了一个Page接口的实现,并添加了我的JsonViews。而不是返回页面,我现在返回MyPage
public class MyPage<T> implements Page<T> {
private Page<T> pageObj;
public MyPage(Page<T> pageObj) {
this.pageObj = pageObj;
}
@JsonView(PaginatedResult.class)
@Override
public int getNumber() {
return pageObj.getNumber();
}
@Override
public int getSize() {
return pageObj.getSize();
}
@JsonView(PaginatedResult.class)
@Override
public int getNumberOfElements() {
return pageObj.getNumberOfElements();
}
@JsonView(PaginatedResult.class)
@Override
public List<T> getContent() {
return pageObj.getContent();
}
@Override
public boolean hasContent() {
return pageObj.hasContent();
}
@Override
public Sort getSort() {
return pageObj.getSort();
}
@JsonView(PaginatedResult.class)
@Override
public boolean isFirst() {
return pageObj.isFirst();
}
@JsonView(PaginatedResult.class)
@Override
public boolean isLast() {
return pageObj.isLast();
}
@Override
public boolean hasNext() {
return pageObj.hasNext();
}
@Override
public boolean hasPrevious() {
return pageObj.hasPrevious();
}
@Override
public Pageable nextPageable() {
return pageObj.nextPageable();
}
@Override
public Pageable previousPageable() {
return pageObj.previousPageable();
}
@Override
public Iterator<T> iterator() {
return pageObj.iterator();
}
@JsonView(PaginatedResult.class)
@Override
public int getTotalPages() {
return pageObj.getTotalPages();
}
@JsonView(PaginatedResult.class)
@Override
public long getTotalElements() {
return pageObj.getTotalElements();
}
}
答案 4 :(得分:1)
您需要递归添加注释@JsonView(TravelRequestView.MyRequests.class)。将其添加到要在Page class中查看的字段中。
public class Page<T> {
@JsonView(TravelRequestView.MyRequests.class)
private T view;
...
}
或为ObjectMapper启用DEFAULT_VIEW_INCLUSION:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="defaultViewInclusion" value="true"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
或使用dto对象作为您可以控制所有观看次数的回复
答案 5 :(得分:0)
更清楚的是告诉杰克逊为一个类型为Page的bean序列化所有道具。 要做到这一点,你只需声明要适应Jackson BeanSerializer:com.fasterxml.jackson.databind.ser.BeanSerializer创建一个扩展BeanSerializer的类,调用PageSerialier,如果bean的类型为Page&lt;&gt;不要应用属性过滤。
如下面的代码所示,我刚刚删除了Page实例的过滤:
public class MyPageSerializer extends BeanSerializer {
/**
* MODIFIED By Gauthier PEEL
*/
@Override
protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonGenerationException {
final BeanPropertyWriter[] props;
// ADDED
// ADDED
// ADDED
if (bean instanceof Page) {
// for Page DO NOT filter anything so that @JsonView is passthrough at this level
props = _props;
} else {
// ADDED
// ADDED
if (_filteredProps != null && provider.getActiveView() != null) {
props = _filteredProps;
} else {
props = _props;
}
}
// rest of the method unchanged
}
// inherited constructor removed for concision
}
然后你需要用模块向杰克逊宣布:
public class MyPageModule extends SimpleModule {
@Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (serializer instanceof BeanSerializerBase) {
return new MyPageSerializer ((BeanSerializerBase) serializer);
}
return serializer;
}
});
}
}
Spring conf现在:在你的@Configuration中创建一个MyPageModule的新@Bean
@Configuration
public class WebConfigPage extends WebMvcConfigurerAdapter {
/**
* To enable Jackson @JsonView to work with Page<T>
*/
@Bean
public MyPageModule myPageModule() {
return new MyPageModule();
}
}
你完成了。