Reputation: 2060
Consider a @PostMapping in Spring MVC, and we want to map the request body to a DTO, as well as other request parameters like query and path variables.
For mapping the request body we can use the @RequestBody annotation on a parameter, which will tell Spring to use the RequestResponseBodyMethodProcessor
.
For mapping the request parameters we can use the @ModelAttribute annotation (or avoid any annotations; same effect), which will tell Spring to use the ServletModelAttributeMethodProcessor
.
But is there an easy way to combine these two? Is there a way to make spring first map the DTO with request parameters and then override with data deserialized from the JSON in the body?
The only way I see it at the moment is to create a custom HandlerMethodArgumentResolver
but I'd like to reuse any existing Spring functionality first.
Upvotes: 4
Views: 805
Reputation: 2060
Turns out no, not at time of writing anyway.
RequestMappingHandlerAdapter.getDefaultArgumentResolvers
holds the default configuration for the argument resovlers, and we have:
RequestResponseBodyMethodProcessor
handling the @RequestBody
annotation, andServletModelAttributeMethodProcessor
handling the @ModelAttribute
, or any parameter without any annotations.It's always just one single resolver that gets executed, even if you try using a composite.
@ControllerAdvice
@RequiredArgsConstructor
@Order(HIGHEST_PRECEDENCE)
public class ServletRequestBinderRequestBodyAdvice extends RequestBodyAdviceAdapter {
private final ServletRequest servletRequest;
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
copyDefaultPropertiesThatWhereOverwritenWithNull(parameter, body);
new ExtendedServletRequestDataBinder(body).bind(servletRequest);
return body;
}
private void copyDefaultPropertiesThatWhereOverwritenWithNull(MethodParameter parameter, Object arg) {
Object argWithDefaults = instantiateClass(parameter.getParameterType());
copyPropertiesSkippingNulls(argWithDefaults, arg);
}
}
Upvotes: 1