From 8c56fec84384148c10662ff997d62c149d46d4ea Mon Sep 17 00:00:00 2001 From: Babalola Opeyemi Daniel Date: Sun, 17 May 2026 21:05:59 +0100 Subject: [PATCH] Return 400 Bad Request when request body converter returns incompatible type. When text/uri-list is posted to a non-association endpoint, UriListHttpMessageConverter returns a CollectionModel instead of the target domain type. The argument resolver then fails with an internal exception mapped to 500. This change validates the converter output type and throws HttpMessageNotReadableException (400) instead. Fixes #1194 Signed-off-by: Babalola Opeyemi Daniel --- ...ResourceHandlerMethodArgumentResolver.java | 5 +++++ ...andlerMethodArgumentResolverUnitTests.java | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolver.java index a72505656..583807bba 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolver.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolver.java @@ -63,6 +63,7 @@ * @author Jon Brisbin * @author Oliver Gierke * @author Mark Paluch + * @author Babalola Opeyemi Daniel */ public class PersistentEntityResourceHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @@ -139,6 +140,10 @@ public boolean supportsParameter(MethodParameter parameter) { throw new HttpMessageNotReadableException(String.format(ERROR_MESSAGE, domainType), request); } + if (!domainType.isInstance(newObject)) { + throw new HttpMessageNotReadableException(String.format(ERROR_MESSAGE, domainType), request); + } + PersistentEntity entity = resourceInformation.getPersistentEntity(); if (!id.isPresent()) { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolverUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolverUnitTests.java index 7d929f3c4..57636024d 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolverUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceHandlerMethodArgumentResolverUnitTests.java @@ -42,7 +42,9 @@ import org.springframework.data.rest.webmvc.support.BackendIdHandlerMethodArgumentResolver; import org.springframework.http.HttpInputMessage; import org.springframework.http.MediaType; +import org.springframework.hateoas.CollectionModel; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.plugin.core.PluginRegistry; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -55,6 +57,7 @@ * Unit tests for {@link PersistentEntityResourceHandlerMethodArgumentResolver}. * * @author Oliver Gierke + * @author Babalola Opeyemi Daniel */ class PersistentEntityResourceHandlerMethodArgumentResolverUnitTests { @@ -124,6 +127,23 @@ Arrays.> asList(converter), rootResourceResolver, backen }); } + @Test // GH-1194 + @SuppressWarnings("unchecked") + void rejectsRequestBodyIfConverterReturnsIncompatibleType() throws Exception { + + PersistentEntityResourceHandlerMethodArgumentResolver argumentResolver = new PersistentEntityResourceHandlerMethodArgumentResolver( + Arrays.> asList(converter), rootResourceResolver, backendIdResolver, reader, + PluginRegistry.empty(), FACTORY); + + HttpServletRequest request = new MockHttpServletRequest("POST", "/foo"); + + doReturn(CollectionModel.empty()).when(converter).read(Mockito.any(Class.class), Mockito.any(HttpInputMessage.class)); + + assertThatExceptionOfType(HttpMessageNotReadableException.class).isThrownBy(() -> { + argumentResolver.resolveArgument(null, null, new ServletWebRequest(request), null); + }); + } + private void setupRootResourceInfoFor(Class type) throws Exception { RootResourceInformation information = mock(RootResourceInformation.class);