Browse Source
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-14165pull/1245/head
10 changed files with 609 additions and 63 deletions
@ -0,0 +1,197 @@
@@ -0,0 +1,197 @@
|
||||
/* |
||||
* Copyright 2002-2016 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 |
||||
* |
||||
* http://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.messaging.simp.annotation.support |
||||
|
||||
import java.util.Collections |
||||
import java.util.HashMap |
||||
|
||||
import org.junit.Before |
||||
import org.junit.Test |
||||
import org.mockito.Mock |
||||
import org.mockito.MockitoAnnotations |
||||
|
||||
import org.springframework.context.support.StaticApplicationContext |
||||
import org.springframework.messaging.Message |
||||
import org.springframework.messaging.MessageChannel |
||||
import org.springframework.messaging.SubscribableChannel |
||||
import org.springframework.messaging.converter.MessageConverter |
||||
import org.springframework.messaging.handler.annotation.Header |
||||
import org.springframework.messaging.handler.annotation.MessageMapping |
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor |
||||
import org.springframework.messaging.simp.SimpMessageSendingOperations |
||||
import org.springframework.messaging.simp.SimpMessagingTemplate |
||||
import org.springframework.messaging.support.MessageBuilder |
||||
import org.springframework.stereotype.Controller |
||||
|
||||
import org.junit.Assert.* |
||||
import org.springframework.messaging.MessageHandlingException |
||||
import org.springframework.messaging.handler.annotation.MessageExceptionHandler |
||||
|
||||
/** |
||||
* Kotlin test fixture for [SimpAnnotationMethodMessageHandler]. |
||||
* |
||||
* @author Sebastien Deleuze |
||||
*/ |
||||
class SimpAnnotationMethodMessageHandlerKotlinTests { |
||||
|
||||
|
||||
lateinit var messageHandler: TestSimpAnnotationMethodMessageHandler |
||||
|
||||
lateinit var testController: TestController |
||||
|
||||
@Mock |
||||
lateinit var channel: SubscribableChannel |
||||
|
||||
@Mock |
||||
lateinit var converter: MessageConverter |
||||
|
||||
@Before |
||||
fun setup() { |
||||
MockitoAnnotations.initMocks(this) |
||||
val brokerTemplate = SimpMessagingTemplate(channel) |
||||
brokerTemplate.messageConverter = converter |
||||
messageHandler = TestSimpAnnotationMethodMessageHandler(brokerTemplate, channel, channel) |
||||
messageHandler.applicationContext = StaticApplicationContext() |
||||
messageHandler.afterPropertiesSet() |
||||
testController = TestController() |
||||
} |
||||
|
||||
@Test |
||||
fun nullableHeaderWithHeader() { |
||||
val message = createMessage("/nullableHeader", Collections.singletonMap("foo", "bar")) |
||||
messageHandler.registerHandler(testController) |
||||
messageHandler.handleMessage(message) |
||||
assertNull(testController.exception) |
||||
assertEquals("bar", testController.header) |
||||
} |
||||
|
||||
@Test |
||||
fun nullableHeaderWithoutHeader() { |
||||
val message = createMessage("/nullableHeader", Collections.emptyMap()) |
||||
messageHandler.registerHandler(testController) |
||||
messageHandler.handleMessage(message) |
||||
assertNull(testController.exception) |
||||
assertNull(testController.header) |
||||
} |
||||
|
||||
@Test |
||||
fun nonNullableHeaderWithHeader() { |
||||
val message = createMessage("/nonNullableHeader", Collections.singletonMap("foo", "bar")) |
||||
messageHandler.registerHandler(testController) |
||||
messageHandler.handleMessage(message) |
||||
assertEquals("bar", testController.header) |
||||
} |
||||
|
||||
@Test |
||||
fun nonNullableHeaderWithoutHeader() { |
||||
val message = createMessage("/nonNullableHeader", Collections.emptyMap()) |
||||
messageHandler.registerHandler(testController) |
||||
messageHandler.handleMessage(message) |
||||
assertNotNull(testController.exception) |
||||
assertTrue(testController.exception is MessageHandlingException) |
||||
} |
||||
|
||||
@Test |
||||
fun nullableHeaderNotRequiredWithHeader() { |
||||
val message = createMessage("/nullableHeaderNotRequired", Collections.singletonMap("foo", "bar")) |
||||
messageHandler.registerHandler(testController) |
||||
messageHandler.handleMessage(message) |
||||
assertNull(testController.exception) |
||||
assertEquals("bar", testController.header) |
||||
} |
||||
|
||||
@Test |
||||
fun nullableHeaderNotRequiredWithoutHeader() { |
||||
val message = createMessage("/nullableHeaderNotRequired", Collections.emptyMap()) |
||||
messageHandler.registerHandler(testController) |
||||
messageHandler.handleMessage(message) |
||||
assertNull(testController.exception) |
||||
assertNull(testController.header) |
||||
} |
||||
|
||||
@Test |
||||
fun nonNullableHeaderNotRequiredWithHeader() { |
||||
val message = createMessage("/nonNullableHeaderNotRequired", Collections.singletonMap("foo", "bar")) |
||||
messageHandler.registerHandler(testController) |
||||
messageHandler.handleMessage(message) |
||||
assertEquals("bar", testController.header) |
||||
} |
||||
|
||||
@Test |
||||
fun nonNullableHeaderNotRequiredWithoutHeader() { |
||||
val message = createMessage("/nonNullableHeaderNotRequired", Collections.emptyMap()) |
||||
messageHandler.registerHandler(testController) |
||||
messageHandler.handleMessage(message) |
||||
assertNotNull(testController.exception) |
||||
assertTrue(testController.exception is IllegalArgumentException) |
||||
} |
||||
|
||||
private fun createMessage(destination: String, headers: Map<String, String?>): Message<ByteArray> { |
||||
val accessor = SimpMessageHeaderAccessor.create() |
||||
accessor.sessionId = "session1" |
||||
accessor.sessionAttributes = HashMap() |
||||
accessor.destination = destination |
||||
for (entry in headers.entries) accessor.setHeader(entry.key, entry.value) |
||||
|
||||
return MessageBuilder.withPayload(ByteArray(0)).setHeaders(accessor).build() |
||||
} |
||||
|
||||
class TestSimpAnnotationMethodMessageHandler(brokerTemplate: SimpMessageSendingOperations, |
||||
clientInboundChannel: SubscribableChannel, |
||||
clientOutboundChannel: MessageChannel) : |
||||
SimpAnnotationMethodMessageHandler(clientInboundChannel, clientOutboundChannel, brokerTemplate) { |
||||
|
||||
fun registerHandler(handler: Any?) { |
||||
super.detectHandlerMethods(handler) |
||||
} |
||||
} |
||||
|
||||
@Suppress("unused") |
||||
@Controller |
||||
@MessageMapping |
||||
class TestController { |
||||
|
||||
var header: String? = null |
||||
var exception: Throwable? = null |
||||
|
||||
@MessageMapping("/nullableHeader") |
||||
fun nullableHeader(@Header("foo") foo: String?) { |
||||
header = foo |
||||
} |
||||
|
||||
@MessageMapping("/nonNullableHeader") |
||||
fun nonNullableHeader(@Header("foo") foo: String) { |
||||
header = foo |
||||
} |
||||
|
||||
@MessageMapping("/nullableHeaderNotRequired") |
||||
fun nullableHeaderNotRequired(@Header("foo", required = false) foo: String?) { |
||||
header = foo |
||||
} |
||||
|
||||
@MessageMapping("/nonNullableHeaderNotRequired") |
||||
fun nonNullableHeaderNotRequired(@Header("foo", required = false) foo: String) { |
||||
header = foo |
||||
} |
||||
|
||||
@MessageExceptionHandler |
||||
fun handleIllegalArgumentException(exception: Throwable) { |
||||
this.exception = exception |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
|
||||
package org.springframework.web.reactive.result.method.annotation |
||||
|
||||
import org.junit.Before |
||||
import org.junit.Test |
||||
import org.springframework.core.MethodParameter |
||||
import org.springframework.core.annotation.SynthesizingMethodParameter |
||||
import org.springframework.format.support.DefaultFormattingConversionService |
||||
import org.springframework.http.HttpMethod |
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest |
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse |
||||
import org.springframework.util.ReflectionUtils |
||||
import org.springframework.web.bind.annotation.RequestParam |
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer |
||||
import org.springframework.web.reactive.BindingContext |
||||
import org.springframework.web.server.ServerWebExchange |
||||
import org.springframework.web.server.ServerWebInputException |
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange |
||||
import org.springframework.web.server.session.MockWebSessionManager |
||||
import reactor.test.StepVerifier |
||||
|
||||
/** |
||||
* Kotlin test fixture for [RequestParamMethodArgumentResolver]. |
||||
* |
||||
* @author Sebastien Deleuze |
||||
*/ |
||||
class RequestParamMethodArgumentResolverKotlinTests { |
||||
|
||||
lateinit var resolver: RequestParamMethodArgumentResolver |
||||
lateinit var exchange: ServerWebExchange |
||||
lateinit var bindingContext: BindingContext |
||||
|
||||
lateinit var nullableParamRequired: MethodParameter |
||||
lateinit var nullableParamNotRequired: MethodParameter |
||||
lateinit var nonNullableParamRequired: MethodParameter |
||||
lateinit var nonNullableParamNotRequired: MethodParameter |
||||
|
||||
|
||||
@Before |
||||
fun setup() { |
||||
resolver = RequestParamMethodArgumentResolver(null, true) |
||||
val request = MockServerHttpRequest(HttpMethod.GET, "/") |
||||
val sessionManager = MockWebSessionManager() |
||||
exchange = DefaultServerWebExchange(request, MockServerHttpResponse(), sessionManager) |
||||
val initializer = ConfigurableWebBindingInitializer() |
||||
initializer.conversionService = DefaultFormattingConversionService() |
||||
bindingContext = BindingContext(initializer) |
||||
|
||||
val method = ReflectionUtils.findMethod(javaClass, "handle", String::class.java, |
||||
String::class.java, String::class.java, String::class.java) |
||||
|
||||
nullableParamRequired = SynthesizingMethodParameter(method, 0) |
||||
nullableParamNotRequired = SynthesizingMethodParameter(method, 1) |
||||
nonNullableParamRequired = SynthesizingMethodParameter(method, 2) |
||||
nonNullableParamNotRequired = SynthesizingMethodParameter(method, 3) |
||||
} |
||||
|
||||
@Test |
||||
fun resolveNullableRequiredWithParameter() { |
||||
exchange.request.queryParams.set("name", "123") |
||||
var result = resolver.resolveArgument(nullableParamRequired, bindingContext, exchange) |
||||
StepVerifier.create(result).expectNext("123").expectComplete().verify() |
||||
} |
||||
|
||||
@Test |
||||
fun resolveNullableRequiredWithoutParameter() { |
||||
var result = resolver.resolveArgument(nullableParamRequired, bindingContext, exchange) |
||||
StepVerifier.create(result).expectComplete().verify() |
||||
} |
||||
|
||||
@Test |
||||
fun resolveNullableNotRequiredWithParameter() { |
||||
exchange.request.queryParams.set("name", "123") |
||||
var result = resolver.resolveArgument(nullableParamNotRequired, bindingContext, exchange) |
||||
StepVerifier.create(result).expectNext("123").expectComplete().verify() |
||||
} |
||||
|
||||
@Test |
||||
fun resolveNullableNotRequiredWithoutParameter() { |
||||
var result = resolver.resolveArgument(nullableParamNotRequired, bindingContext, exchange) |
||||
StepVerifier.create(result).expectComplete().verify() |
||||
} |
||||
|
||||
@Test |
||||
fun resolveNonNullableRequiredWithParameter() { |
||||
exchange.request.queryParams.set("name", "123") |
||||
var result = resolver.resolveArgument(nonNullableParamRequired, bindingContext, exchange) |
||||
StepVerifier.create(result).expectNext("123").expectComplete().verify() |
||||
} |
||||
|
||||
@Test |
||||
fun resolveNonNullableRequiredWithoutParameter() { |
||||
var result = resolver.resolveArgument(nonNullableParamRequired, bindingContext, exchange) |
||||
StepVerifier.create(result).expectError(ServerWebInputException::class.java).verify() |
||||
} |
||||
|
||||
@Test |
||||
fun resolveNonNullableNotRequiredWithParameter() { |
||||
exchange.request.queryParams.set("name", "123") |
||||
var result = resolver.resolveArgument(nonNullableParamNotRequired, bindingContext, exchange) |
||||
StepVerifier.create(result).expectNext("123").expectComplete().verify() |
||||
} |
||||
|
||||
@Test |
||||
fun resolveNonNullableNotRequiredWithoutParameter() { |
||||
var result = resolver.resolveArgument(nonNullableParamNotRequired, bindingContext, exchange) |
||||
StepVerifier.create(result).expectComplete().verify() |
||||
} |
||||
|
||||
|
||||
@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) { |
||||
} |
||||
|
||||
} |
||||
|
||||
@ -0,0 +1,218 @@
@@ -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) { |
||||
} |
||||
|
||||
} |
||||
|
||||
Loading…
Reference in new issue