Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make BodyBuilder.bodyValueAndAwait() a reified type of function #32652

Conversation

GeorgePap-719
Copy link
Contributor

@GeorgePap-719 GeorgePap-719 commented Apr 16, 2024

Summary

ServerResponse.BodyBuilder.bodyValueAndAwait API is broken when it is used with an application it
uses for serialization the kotlinx.serialization library and the provided body uses generics.
The library is unable to serialize the body since it is a type of Any and spring falls back
to default one. This could lead to unexpected responses.

For example:

@Serializable
data class SomeBody(@SerialName("user_id") val userId: Int, val name: String)

suspend fun returnBody(request: ServerRequest): ServerResponse {
    return ServerResponse
        .ok()
        // Note: `fun ServerResponse.BodyBuilder.bodyValueAndAwait(body: Any)`
        // is not a reified function and is subject to type erasure.
        // In cases where response is a collection, the object is serialized as `Collection<Any>`,
        // and the response does not have the proper format, which is not expected.
        // Actual response: [{"userId":1,"name":"name"}]
        // Expected response : [{"user_id":1,"name":"name"}]
        .bodyValueAndAwait(listOf(SomeBody(1, "name")))
}

A minimum reproducible example can be found here.

Proposal

Make BodyBuilder.bodyValueAndAwait() a reified type of function. We can also migrate the API without
breaking existing functionality.

Alternatives

Only document the behavior and accept it as the intended behavior.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 16, 2024
@jhoeller jhoeller added in: web Issues in web modules (web, webmvc, webflux, websocket) theme: kotlin An issue related to Kotlin support labels Apr 17, 2024
@sdeleuze sdeleuze self-assigned this Apr 24, 2024
message = "Use `bodyValueAndAwait<T>()` instead, which is not subject to type erasure.",
level = DeprecationLevel.WARNING
)
@JvmName("bodyValueAndAwait0") // to avoid platform declaration clash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide more details on the issue you see if you remove this @JvmName("bodyValueAndAwait0") annotation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove this annotation then the compilation will fail with error:

Platform declaration clash: The following declarations have the same JVM signature (bodyValueAndAwait(Lorg/springframework/web/reactive/function/server/ServerResponse$BodyBuilder;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;):
    suspend inline fun <reified T : Any> ServerResponse.BodyBuilder.bodyValueAndAwait(body: T, `$completion`: Continuation<ServerResponse>): Any? defined in org.springframework.web.reactive.function.server
    suspend fun ServerResponse.BodyBuilder.bodyValueAndAwait(body: Any, `$completion`: Continuation<ServerResponse>): Any? defined in org.springframework.web.reactive.function.server

When the bytecode is generated the generics are erased so the compiler just sees two functions with the same type, because in the end T is replaced with Any.

Function `bodyValueAndAwait(body: Any)` is not a reified function and is
subject to type erasure. If an application uses for serialization the
`kotlinx.serialization` library and the provided body uses generics,
then the library is unable to serialize the body and spring falls back
to default one. This could lead to unexpected behaviors.

Signed-off-by: George Papadopoulos <george.719pap@gmail.com>
@GeorgePap-719
Copy link
Contributor Author

Sorry for force-push while in-review. Minor changes in KDocs and error message.

@sdeleuze
Copy link
Contributor

After a deeper look, I am going to implement this feature differently on both Java and Kotlin side via #32713 which supersedes this issue.

@sdeleuze sdeleuze closed this Apr 26, 2024
@sdeleuze sdeleuze added status: superseded An issue that has been superseded by another and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Apr 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: superseded An issue that has been superseded by another theme: kotlin An issue related to Kotlin support
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants