Fix bean validation on suspending function parameters
This commit leverages Hibernate Validator's own internal use of standard Java reflection to perform validation on suspending function, which fixes the ArrayIndexOutOfBoundsException previously observed. Validation of suspending function return values remains unsupported as Hibernate Validator is not Coroutines aware. Closes gh-23499
This commit is contained in:
parent
9c6fd3ed06
commit
89c7c6e9dd
|
@ -36,6 +36,7 @@ dependencies {
|
|||
testImplementation("org.apache.commons:commons-pool2")
|
||||
testImplementation("org.awaitility:awaitility")
|
||||
testImplementation("jakarta.inject:jakarta.inject-tck")
|
||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
|
||||
testRuntimeOnly("jakarta.xml.bind:jakarta.xml.bind-api")
|
||||
testRuntimeOnly("org.glassfish:jakarta.el")
|
||||
// Substitute for javax.management:jmxremote_optional:1.0.1_04 (not available on Maven Central)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
|
@ -49,8 +49,6 @@ import org.springframework.beans.factory.InitializingBean;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.core.KotlinReflectionParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
@ -75,6 +73,7 @@ import org.springframework.util.ReflectionUtils;
|
|||
* {@code jakarta.validation} API being present but no explicit Validator having been configured.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
* @since 3.0
|
||||
* @see jakarta.validation.ValidatorFactory
|
||||
* @see jakarta.validation.Validator
|
||||
|
@ -118,13 +117,6 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
|||
private ValidatorFactory validatorFactory;
|
||||
|
||||
|
||||
public LocalValidatorFactoryBean() {
|
||||
if (KotlinDetector.isKotlinReflectPresent()) {
|
||||
this.parameterNameDiscoverer = new KotlinReflectionParameterNameDiscoverer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the desired provider class, if any.
|
||||
* <p>If not specified, JSR-303's default search mechanism will be used.
|
||||
|
@ -196,9 +188,8 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
|||
/**
|
||||
* Set the ParameterNameDiscoverer to use for resolving method and constructor
|
||||
* parameter names if needed for message interpolation.
|
||||
* <p>Default is Hibernate Validator's own internal use of standard Java reflection,
|
||||
* with an additional {@link KotlinReflectionParameterNameDiscoverer} if Kotlin
|
||||
* is present. This may be overridden with a custom subclass or a Spring-controlled
|
||||
* <p>Default is Hibernate Validator's own internal use of standard Java reflection.
|
||||
* This may be overridden with a custom subclass or a Spring-controlled
|
||||
* {@link org.springframework.core.DefaultParameterNameDiscoverer} if necessary.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2002-2023 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
|
||||
*
|
||||
* https://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.validation.beanvalidation
|
||||
|
||||
import jakarta.validation.ValidationException
|
||||
import jakarta.validation.Validator
|
||||
import jakarta.validation.constraints.NotEmpty
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.aop.framework.ProxyFactory
|
||||
import org.springframework.validation.annotation.Validated
|
||||
|
||||
/**
|
||||
* Kotlin tests for [MethodValidationInterceptor] + [LocalValidatorFactoryBean].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
class KotlinMethodValidationTests {
|
||||
|
||||
@Test
|
||||
fun parameterValidation() {
|
||||
val bean = MyValidBean()
|
||||
val proxyFactory = ProxyFactory(bean)
|
||||
val validator = LocalValidatorFactoryBean()
|
||||
validator.afterPropertiesSet()
|
||||
proxyFactory.addAdvice(MethodValidationInterceptor(validator as Validator))
|
||||
val proxy = proxyFactory.getProxy() as MyValidBean
|
||||
Assertions.assertThat(proxy.validName("name")).isEqualTo("name")
|
||||
Assertions.assertThatExceptionOfType(ValidationException::class.java).isThrownBy {
|
||||
proxy.validName("")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun coroutinesParameterValidation() = runBlocking<Unit> {
|
||||
val bean = MyValidCoroutinesBean()
|
||||
val proxyFactory = ProxyFactory(bean)
|
||||
val validator = LocalValidatorFactoryBean()
|
||||
validator.afterPropertiesSet()
|
||||
proxyFactory.addAdvice(MethodValidationInterceptor(validator as Validator))
|
||||
val proxy = proxyFactory.getProxy() as MyValidCoroutinesBean
|
||||
Assertions.assertThat(proxy.validName("name")).isEqualTo("name")
|
||||
Assertions.assertThatExceptionOfType(ValidationException::class.java).isThrownBy {
|
||||
runBlocking {
|
||||
proxy.validName("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Validated
|
||||
open class MyValidBean {
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
open fun validName(@NotEmpty name: String) = name
|
||||
}
|
||||
|
||||
@Validated
|
||||
open class MyValidCoroutinesBean {
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
open suspend fun validName(@NotEmpty name: String) = name
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue