Treat kotlin.Unit as void in web controllers

This commit fixes a regression introduced by gh-21139
via the usage of Kotlin reflection to invoke HTTP
handler methods. It ensures that kotlin.Unit is treated
as void by returning null.

It also polishes CoroutinesUtils to have a consistent
handling compared to the regular case, and adds related
tests to prevent future regressions.

Closes gh-31648
This commit is contained in:
Sébastien Deleuze
2023-11-22 12:40:32 +01:00
parent c329ed83ef
commit 441e210533
6 changed files with 79 additions and 4 deletions

View File

@@ -26,6 +26,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
import kotlin.Unit;
import kotlin.coroutines.CoroutineContext;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
@@ -326,7 +327,8 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
}
}
return function.callBy(argMap);
Object result = function.callBy(argMap);
return (result == Unit.INSTANCE ? null : result);
}
}
}

View File

@@ -164,6 +164,20 @@ class InvocableHandlerMethodKotlinTests {
assertHandlerResultValue(result, "override")
}
@Test
fun unitReturnValue() {
val method = NullResultController::unit.javaMethod!!
val result = invoke(NullResultController(), method)
assertHandlerResultValue(result, null)
}
@Test
fun nullReturnValue() {
val method = NullResultController::nullable.javaMethod!!
val result = invoke(NullResultController(), method)
assertHandlerResultValue(result, null)
}
private fun invokeForResult(handler: Any, method: Method, vararg providedArgs: Any): HandlerResult? {
return invoke(handler, method, *providedArgs).block(Duration.ofSeconds(5))
@@ -186,7 +200,7 @@ class InvocableHandlerMethodKotlinTests {
return resolver
}
private fun assertHandlerResultValue(mono: Mono<HandlerResult>, expected: String) {
private fun assertHandlerResultValue(mono: Mono<HandlerResult>, expected: String?) {
StepVerifier.create(mono)
.consumeNextWith {
if (it.returnValue is Mono<*>) {
@@ -242,4 +256,14 @@ class InvocableHandlerMethodKotlinTests {
@Suppress("RedundantSuspendModifier")
suspend fun handleSuspending(@RequestParam value: String = "default") = value
}
class NullResultController {
fun unit() {
}
fun nullable(): String? {
return null
}
}
}