I am using Spring data pagination in my REST Controller and returning Paged entity. I would like to control the data returned as JSON with the help of JSONViews.
I am able to achieve the result when I return a single object. But when I return Page, I am receiving blank JSON as response.
Following is my method signature.
@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)
I am able to receive response when I remove @JsonView annotation.
If you are using spring-boot, then another simpler solution would be add the following to application.yml
spring:
jackson:
mapper:
DEFAULT_VIEW_INCLUSION: true
or application.properties
spring.jackson.mapper.DEFAULT_VIEW_INCLUSION=true
With this approach, we have the advantage of retaining the ObjectMapper
managed by Spring Container and not creating a new ObjectMapper. So once we use the spring managed ObjectMapper, then any Custom Serialzers we define will still continue to work e.g CustomDateSerialzer
Reference: http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html
Setting DEFAULT_VIEW_INCLUSION has a global effect, while all we need is to be able to serialize a Page object. The following code will register a serializer for Page, and is a simple change to your code:
@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();
}
});
}
Another (arguably more elegant) solution is to register the following ResponseBodyAdvice. It will make sure your REST endpoint will still return a JSON array, and set a HTTP header 'X-Has-Next-Page' to indicate whether there is more data. The advantages are: 1) No extra count(*) query to your DB (single query) 2) Response is more elegant, since it returns a JSON array
/**
* 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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With