Polish Kotlin nullable support

This commit polishes Kotlin nullable support by reusing
MethodParameter#isOptional() instead of adding a new
MethodParameter#isNullable() method, adds
Kotlin tests and introduces Spring Web Reactive
support.

Issue: SPR-14165
This commit is contained in:
Sebastien Deleuze
2016-11-21 23:59:41 +01:00
parent fada91e538
commit a143b57d4b
10 changed files with 609 additions and 63 deletions

View File

@@ -100,7 +100,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional() && !nestedParameter.isNullable()) {
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());

View File

@@ -0,0 +1,218 @@
package org.springframework.web.method.annotation
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.springframework.core.MethodParameter
import org.springframework.core.annotation.SynthesizingMethodParameter
import org.springframework.core.convert.support.DefaultConversionService
import org.springframework.http.HttpMethod
import org.springframework.http.MediaType
import org.springframework.mock.web.test.MockHttpServletRequest
import org.springframework.mock.web.test.MockHttpServletResponse
import org.springframework.mock.web.test.MockMultipartFile
import org.springframework.mock.web.test.MockMultipartHttpServletRequest
import org.springframework.util.ReflectionUtils
import org.springframework.web.bind.MissingServletRequestParameterException
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer
import org.springframework.web.bind.support.DefaultDataBinderFactory
import org.springframework.web.bind.support.WebDataBinderFactory
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.web.context.request.ServletWebRequest
import org.springframework.web.multipart.MultipartFile
import org.springframework.web.multipart.support.MissingServletRequestPartException
/**
* Kotlin test fixture for [RequestParamMethodArgumentResolver].
*
* @author Sebastien Deleuze
*/
class RequestParamMethodArgumentResolverKotlinTests {
lateinit var resolver: RequestParamMethodArgumentResolver
lateinit var webRequest: NativeWebRequest
lateinit var binderFactory: WebDataBinderFactory
lateinit var request: MockHttpServletRequest
lateinit var nullableParamRequired: MethodParameter
lateinit var nullableParamNotRequired: MethodParameter
lateinit var nonNullableParamRequired: MethodParameter
lateinit var nonNullableParamNotRequired: MethodParameter
lateinit var nullableMultipartParamRequired: MethodParameter
lateinit var nullableMultipartParamNotRequired: MethodParameter
lateinit var nonNullableMultipartParamRequired: MethodParameter
lateinit var nonNullableMultipartParamNotRequired: MethodParameter
@Before
fun setup() {
resolver = RequestParamMethodArgumentResolver(null, true)
request = MockHttpServletRequest()
val initializer = ConfigurableWebBindingInitializer()
initializer.conversionService = DefaultConversionService()
binderFactory = DefaultDataBinderFactory(initializer)
webRequest = ServletWebRequest(request, MockHttpServletResponse())
val method = ReflectionUtils.findMethod(javaClass, "handle", String::class.java,
String::class.java, String::class.java, String::class.java,
MultipartFile::class.java, MultipartFile::class.java,
MultipartFile::class.java, MultipartFile::class.java)
nullableParamRequired = SynthesizingMethodParameter(method, 0)
nullableParamNotRequired = SynthesizingMethodParameter(method, 1)
nonNullableParamRequired = SynthesizingMethodParameter(method, 2)
nonNullableParamNotRequired = SynthesizingMethodParameter(method, 3)
nullableMultipartParamRequired = SynthesizingMethodParameter(method, 4)
nullableMultipartParamNotRequired = SynthesizingMethodParameter(method, 5)
nonNullableMultipartParamRequired = SynthesizingMethodParameter(method, 6)
nonNullableMultipartParamNotRequired = SynthesizingMethodParameter(method, 7)
}
@Test
fun resolveNullableRequiredWithParameter() {
request.addParameter("name", "123")
var result = resolver.resolveArgument(nullableParamRequired, null, webRequest, binderFactory)
assertEquals("123", result)
}
@Test
fun resolveNullableRequiredWithoutParameter() {
var result = resolver.resolveArgument(nullableParamRequired, null, webRequest, binderFactory)
assertNull(result)
}
@Test
fun resolveNullableNotRequiredWithParameter() {
request.addParameter("name", "123")
var result = resolver.resolveArgument(nullableParamNotRequired, null, webRequest, binderFactory)
assertEquals("123", result)
}
@Test
fun resolveNullableNotRequiredWithoutParameter() {
var result = resolver.resolveArgument(nullableParamNotRequired, null, webRequest, binderFactory)
assertNull(result)
}
@Test
fun resolveNonNullableRequiredWithParameter() {
request.addParameter("name", "123")
var result = resolver.resolveArgument(nonNullableParamRequired, null, webRequest, binderFactory)
assertEquals("123", result)
}
@Test(expected = MissingServletRequestParameterException::class)
fun resolveNonNullableRequiredWithoutParameter() {
resolver.resolveArgument(nonNullableParamRequired, null, webRequest, binderFactory)
}
@Test
fun resolveNonNullableNotRequiredWithParameter() {
request.addParameter("name", "123")
var result = resolver.resolveArgument(nonNullableParamNotRequired, null, webRequest, binderFactory)
assertEquals("123", result)
}
@Test(expected = TypeCastException::class)
fun resolveNonNullableNotRequiredWithoutParameter() {
resolver.resolveArgument(nonNullableParamNotRequired, null, webRequest, binderFactory) as String
}
@Test
fun resolveNullableRequiredWithMultipartParameter() {
val request = MockMultipartHttpServletRequest()
val expected = MockMultipartFile("mfile", "Hello World".toByteArray())
request.addFile(expected)
webRequest = ServletWebRequest(request)
var result = resolver.resolveArgument(nullableMultipartParamRequired, null, webRequest, binderFactory)
assertEquals(expected, result)
}
@Test
fun resolveNullableRequiredWithoutMultipartParameter() {
request.method = HttpMethod.POST.name
request.contentType = MediaType.MULTIPART_FORM_DATA_VALUE
var result = resolver.resolveArgument(nullableMultipartParamRequired, null, webRequest, binderFactory)
assertNull(result)
}
@Test
fun resolveNullableNotRequiredWithMultipartParameter() {
val request = MockMultipartHttpServletRequest()
val expected = MockMultipartFile("mfile", "Hello World".toByteArray())
request.addFile(expected)
webRequest = ServletWebRequest(request)
var result = resolver.resolveArgument(nullableMultipartParamNotRequired, null, webRequest, binderFactory)
assertEquals(expected, result)
}
@Test
fun resolveNullableNotRequiredWithoutMultipartParameter() {
request.method = HttpMethod.POST.name
request.contentType = MediaType.MULTIPART_FORM_DATA_VALUE
var result = resolver.resolveArgument(nullableMultipartParamNotRequired, null, webRequest, binderFactory)
assertNull(result)
}
@Test
fun resolveNonNullableRequiredWithMultipartParameter() {
val request = MockMultipartHttpServletRequest()
val expected = MockMultipartFile("mfile", "Hello World".toByteArray())
request.addFile(expected)
webRequest = ServletWebRequest(request)
var result = resolver.resolveArgument(nonNullableMultipartParamRequired, null, webRequest, binderFactory)
assertEquals(expected, result)
}
@Test(expected = MissingServletRequestPartException::class)
fun resolveNonNullableRequiredWithoutMultipartParameter() {
request.method = HttpMethod.POST.name
request.contentType = MediaType.MULTIPART_FORM_DATA_VALUE
resolver.resolveArgument(nonNullableMultipartParamRequired, null, webRequest, binderFactory)
}
@Test
fun resolveNonNullableNotRequiredWithMultipartParameter() {
val request = MockMultipartHttpServletRequest()
val expected = MockMultipartFile("mfile", "Hello World".toByteArray())
request.addFile(expected)
webRequest = ServletWebRequest(request)
var result = resolver.resolveArgument(nonNullableMultipartParamNotRequired, null, webRequest, binderFactory)
assertEquals(expected, result)
}
@Test(expected = TypeCastException::class)
fun resolveNonNullableNotRequiredWithoutMultipartParameter() {
request.method = HttpMethod.POST.name
request.contentType = MediaType.MULTIPART_FORM_DATA_VALUE
resolver.resolveArgument(nonNullableMultipartParamNotRequired, null, webRequest, binderFactory) as MultipartFile
}
@Suppress("unused_parameter")
fun handle(
@RequestParam("name") nullableParamRequired: String?,
@RequestParam("name", required = false) nullableParamNotRequired: String?,
@RequestParam("name") nonNullableParamRequired: String,
@RequestParam("name", required = false) nonNullableParamNotRequired: String,
@RequestParam("mfile") nullableMultipartParamRequired: MultipartFile?,
@RequestParam("mfile", required = false) nullableMultipartParamNotRequired: MultipartFile?,
@RequestParam("mfile") nonNullableMultipartParamRequired: MultipartFile,
@RequestParam("mfile", required = false) nonNullableMultipartParamNotRequired: MultipartFile) {
}
}