Fix HttpEntity support with Kotlin Serialization
This commit adds HttpEntity type unwrapping logic to KotlinRequestBodyAdvice and KotlinResponseBodyAdvice. Closes gh-35281
This commit is contained in:
parent
0d2a0d7b9e
commit
3dc2aa79a4
|
@ -28,6 +28,7 @@ import kotlin.reflect.jvm.ReflectJvmMapping;
|
|||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.converter.AbstractKotlinSerializationHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.SmartHttpMessageConverter;
|
||||
|
@ -61,6 +62,10 @@ public class KotlinRequestBodyAdvice extends RequestBodyAdviceAdapter {
|
|||
for (KParameter p : Objects.requireNonNull(function).getParameters()) {
|
||||
if (KParameter.Kind.VALUE.equals(p.getKind())) {
|
||||
if (index == i++) {
|
||||
if (HttpEntity.class.isAssignableFrom(parameter.getParameterType())) {
|
||||
return Collections.singletonMap(KType.class.getName(),
|
||||
Objects.requireNonNull(p.getType().getArguments().get(0).getType()));
|
||||
}
|
||||
return Collections.singletonMap(KType.class.getName(), p.getType());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import kotlin.reflect.jvm.ReflectJvmMapping;
|
|||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.AbstractKotlinSerializationHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
|
@ -61,6 +62,9 @@ public class KotlinResponseBodyAdvice implements ResponseBodyAdvice<Object> {
|
|||
|
||||
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(Objects.requireNonNull(returnType.getMethod()));
|
||||
KType type = Objects.requireNonNull(function).getReturnType();
|
||||
if (HttpEntity.class.isAssignableFrom(returnType.getParameterType())) {
|
||||
return Collections.singletonMap(KType.class.getName(), Objects.requireNonNull(type.getArguments().get(0).getType()));
|
||||
}
|
||||
return Collections.singletonMap(KType.class.getName(), type);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import kotlinx.serialization.Serializable
|
|||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.core.MethodParameter
|
||||
import org.springframework.http.RequestEntity
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.http.converter.StringHttpMessageConverter
|
||||
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
|
||||
|
@ -68,6 +70,22 @@ class RequestResponseBodyMethodProcessorKotlinTests {
|
|||
.contains("\"value\":\"foo\"")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun writeEntityWithKotlinSerializationJsonMessageConverter() {
|
||||
val method = SampleController::writeMessageEntity::javaMethod.get()!!
|
||||
val handlerMethod = HandlerMethod(SampleController(), method)
|
||||
val methodReturnType = handlerMethod.returnType
|
||||
|
||||
val converters = listOf(KotlinSerializationJsonHttpMessageConverter())
|
||||
val processor = RequestResponseBodyMethodProcessor(converters, null, listOf(KotlinResponseBodyAdvice()))
|
||||
|
||||
val returnValue: Any? = SampleController().writeMessageEntity().body
|
||||
processor.handleReturnValue(returnValue, methodReturnType, this.container, this.request)
|
||||
|
||||
Assertions.assertThat(this.servletResponse.contentAsString)
|
||||
.contains("\"value\":\"foo\"")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun writeGenericTypeWithKotlinSerializationJsonMessageConverter() {
|
||||
val method = SampleController::writeMessages::javaMethod.get()!!
|
||||
|
@ -118,6 +136,24 @@ class RequestResponseBodyMethodProcessorKotlinTests {
|
|||
Assertions.assertThat(result).isEqualTo(Message("foo"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun readEntityWithKotlinSerializationJsonMessageConverter() {
|
||||
val content = "{\"value\" : \"foo\"}"
|
||||
this.servletRequest.setContent(content.toByteArray(StandardCharsets.UTF_8))
|
||||
this.servletRequest.setContentType("application/json")
|
||||
|
||||
val converters = listOf(StringHttpMessageConverter(), KotlinSerializationJsonHttpMessageConverter())
|
||||
val processor = RequestResponseBodyMethodProcessor(converters, null, listOf(KotlinRequestBodyAdvice()))
|
||||
|
||||
val method = SampleController::readMessageEntity::javaMethod.get()!!
|
||||
val methodParameter = MethodParameter(method, 0)
|
||||
|
||||
val result = processor.resolveArgument(methodParameter, container, request, factory) as Message
|
||||
|
||||
Assertions.assertThat(result).isEqualTo(Message("foo"))
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Test
|
||||
fun readGenericTypeWithKotlinSerializationJsonMessageConverter() {
|
||||
|
@ -161,6 +197,10 @@ class RequestResponseBodyMethodProcessorKotlinTests {
|
|||
@ResponseBody
|
||||
fun writeMessage() = Message("foo")
|
||||
|
||||
@RequestMapping
|
||||
@ResponseBody
|
||||
fun writeMessageEntity() = ResponseEntity.ok(Message("foo"))
|
||||
|
||||
@RequestMapping
|
||||
@ResponseBody
|
||||
fun writeMessages() = listOf(Message("foo"), Message("bar"))
|
||||
|
@ -169,6 +209,10 @@ class RequestResponseBodyMethodProcessorKotlinTests {
|
|||
@ResponseBody
|
||||
fun readMessage(message: Message) = message.value
|
||||
|
||||
@RequestMapping
|
||||
@ResponseBody
|
||||
fun readMessageEntity(entity: RequestEntity<Message>) = entity.body!!.value
|
||||
|
||||
@RequestMapping
|
||||
@ResponseBody
|
||||
fun readMessages(messages: List<Message>) = messages.map { it.value }.reduce { acc, string -> "$acc $string" }
|
||||
|
|
Loading…
Reference in New Issue