mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Fix context class resolution for nested types
This commit introduces ContextClassRequestBodyAdvice which adds a "contextClass" hint allowing to resolve generics for Optional, HttpEntity or ServerSentEvent container types. Closes gh-36111
This commit is contained in:
+2
-1
@@ -313,7 +313,8 @@ public abstract class AbstractJacksonHttpMessageConverter<T extends ObjectMapper
|
||||
public Object read(ResolvableType type, HttpInputMessage inputMessage, @Nullable Map<String, Object> hints)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
|
||||
Class<?> contextClass = (type.getSource() instanceof MethodParameter parameter ? parameter.getContainingClass() : null);
|
||||
Class<?> contextClass = (type.getSource() instanceof MethodParameter parameter ? parameter.getContainingClass() :
|
||||
(hints != null ? (Class<?>) hints.get("contextClass") : null));
|
||||
JavaType javaType = getJavaType(type.getType(), contextClass);
|
||||
return readJavaType(javaType, inputMessage, hints);
|
||||
}
|
||||
|
||||
+2
@@ -83,6 +83,7 @@ import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ContextClassRequestBodyAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.JsonViewRequestBodyAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice;
|
||||
@@ -661,6 +662,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
List<ResponseBodyAdvice<?>> responseBodyAdvices = new ArrayList<>(2);
|
||||
if (JACKSON_PRESENT || JACKSON_2_PRESENT) {
|
||||
requestBodyAdvices.add(new JsonViewRequestBodyAdvice());
|
||||
requestBodyAdvices.add(new ContextClassRequestBodyAdvice());
|
||||
responseBodyAdvices.add(new JsonViewResponseBodyAdvice());
|
||||
}
|
||||
if (KOTLIN_REFLECT_PRESENT && KOTLIN_SERIALIZATION_PRESENT) {
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.SmartHttpMessageConverter;
|
||||
|
||||
/**
|
||||
* A {@link RequestBodyAdvice} implementation that adds a {@code "contextClass"} hint for {@link Optional},
|
||||
* {@link HttpEntity} and {@link ServerSentEvent} container types.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0.3
|
||||
*/
|
||||
public class ContextClassRequestBodyAdvice extends RequestBodyAdviceAdapter {
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
if (!AbstractJacksonHttpMessageConverter.class.isAssignableFrom(converterType)) {
|
||||
return false;
|
||||
}
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
return Optional.class.isAssignableFrom(parameterType) || HttpEntity.class.isAssignableFrom(parameterType) ||
|
||||
ServerSentEvent.class.isAssignableFrom(parameterType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> determineReadHints(MethodParameter parameter, Type targetType,
|
||||
Class<? extends SmartHttpMessageConverter<?>> converterType) {
|
||||
|
||||
return Collections.singletonMap("contextClass", parameter.getContainingClass());
|
||||
}
|
||||
}
|
||||
+6
-4
@@ -79,6 +79,7 @@ import org.springframework.web.servlet.handler.ConversionServiceExposingIntercep
|
||||
import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
|
||||
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
|
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ContextClassRequestBodyAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.JsonViewRequestBodyAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice;
|
||||
@@ -208,11 +209,12 @@ class WebMvcConfigurationSupportTests {
|
||||
DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(adapter);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> bodyAdvice = (List<Object>) fieldAccessor.getPropertyValue("requestResponseBodyAdvice");
|
||||
assertThat(bodyAdvice).hasSize(4);
|
||||
assertThat(bodyAdvice).hasSize(5);
|
||||
assertThat(bodyAdvice.get(0).getClass()).isEqualTo(JsonViewRequestBodyAdvice.class);
|
||||
assertThat(bodyAdvice.get(1).getClass()).isEqualTo(KotlinRequestBodyAdvice.class);
|
||||
assertThat(bodyAdvice.get(2).getClass()).isEqualTo(JsonViewResponseBodyAdvice.class);
|
||||
assertThat(bodyAdvice.get(3).getClass()).isEqualTo(KotlinResponseBodyAdvice.class);
|
||||
assertThat(bodyAdvice.get(1).getClass()).isEqualTo(ContextClassRequestBodyAdvice.class);
|
||||
assertThat(bodyAdvice.get(2).getClass()).isEqualTo(KotlinRequestBodyAdvice.class);
|
||||
assertThat(bodyAdvice.get(3).getClass()).isEqualTo(JsonViewResponseBodyAdvice.class);
|
||||
assertThat(bodyAdvice.get(4).getClass()).isEqualTo(KotlinResponseBodyAdvice.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-3
@@ -30,7 +30,6 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -152,7 +151,6 @@ public class HttpEntityMethodProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Determine why this fails with JacksonJsonHttpMessageConverter but passes with MappingJackson2HttpMessageConverter")
|
||||
void resolveArgumentTypeVariable() throws Exception {
|
||||
Method method = MySimpleParameterizedController.class.getMethod("handleDto", HttpEntity.class);
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new MySimpleParameterizedController(), method);
|
||||
@@ -164,7 +162,9 @@ public class HttpEntityMethodProcessorTests {
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||
converters.add(new JacksonJsonHttpMessageConverter());
|
||||
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
|
||||
List<Object> requestResponseBodyAdvice = new ArrayList<>();
|
||||
requestResponseBodyAdvice.add(new ContextClassRequestBodyAdvice());
|
||||
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters, requestResponseBodyAdvice);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
HttpEntity<SimpleBean> result = (HttpEntity<SimpleBean>)
|
||||
|
||||
Reference in New Issue
Block a user