Skip to content

EnableSpringDataWebSupport.pageSerializationMode warning and the numerous reported issues #3369

@xDeyan

Description

@xDeyan

With the change added in #3024 there seems to be couple of reported inconveniences for some users (#3264, #3320, #3327)

I am specifically opening this issue to discuss on what was the initial idea of this change (for which an input from @odrotbohm would be much appreciated) and trying to find a different solution for the problem. I completely agree that providing a stable JSON representation is very important and at the same time allow the implementation PageImpl to evolve independently.

Currently it only targets Jackson as a json serialization library, but AFAIK Gson and others are also supported by the Spring Web project.

So I am having the following questions:

  • Was Jackson the primary target ? Or, since probably it is used 99+% of the time, the change was implemented for it only ?
  • Was it targeting a direct PageImpl serialization only ? Because current implementation also affects a nested property of any object that is of the PageImpl type.

My overall issue is that now, when I opt in for the pageSerializationMode=VIA_DTO, I am breaking the clients of my API. Which is also true, if the PageImpl suddently changes (which was the primary goal of the initial change - stability).

I am currently thinking if we could use ResponseBodyAdvice that will apply the conversion from Page to PagedModel. This will give us the following benefits:

  1. The conversion will be independent from the serialization library (Even tho we can still target only Jackson initially by using a AbstractMappingJacksonResponseBodyAdvice)
  2. This will not affect serialization of bean properties of type PageImpl, only directly returned objects from @Controller
  3. The "converter" can be chosen/configured by the user/dev and the API stability will be transferred away from the maintainers

Potential implementation:

@Configuration(proxyBeanMethods = false) public class SpringDataWebConfiguration implements WebMvcConfigurer, BeanClassLoaderAware { ..... @Bean ResponseBodyAdvice<Object> pageResponseBodyAdvice(@Autowired(required = false) SpringDataWebSettings settings, ObjectProvider<PageResponseBodyConverter> pageConverter) { if (settings == null || settings.pageSerializationMode() == DIRECT) { return PageResponseBodyAdvice.NO_OP;	} return new PageResponseBodyAdvice(pageConverter.getIfAvailable(() -> PagedModel::new)); } public interface PageResponseBodyConverter { <T> Object convert(Page<T> page); } @ControllerAdvice static final class PageResponseBodyAdvice extends AbstractMappingJacksonResponseBodyAdvice { static final ResponseBodyAdvice<Object> NO_OP = new ResponseBodyAdvice<>() { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return false; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { return body; } }; private final PageResponseBodyConverter converter; public PageResponseBodyAdvice(PageResponseBodyConverter converter) { this.converter = converter; } @Override public void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) { Object model = converter.convert((PageImpl<?>) bodyContainer.getValue()); bodyContainer.setValue(model); } @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return super.supports(returnType, converterType) && returnType.getParameterType().isAssignableFrom(PageImpl.class); } } }

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions