Add MethodValidationResult
See gh-29825
This commit is contained in:
committed by
rstoyanchev
parent
8b53fecc06
commit
cc8361cd93
@@ -18,6 +18,7 @@ package org.springframework.validation.beanvalidation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -73,6 +74,8 @@ public class MethodValidationAdapter {
|
||||
|
||||
private static final Comparator<ParameterValidationResult> RESULT_COMPARATOR = new ResultComparator();
|
||||
|
||||
private static final MethodValidationResult EMPTY_RESULT = new EmptyMethodValidationResult();
|
||||
|
||||
|
||||
private final Supplier<Validator> validator;
|
||||
|
||||
@@ -186,15 +189,19 @@ public class MethodValidationAdapter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given method arguments and raise {@link ConstraintViolation}
|
||||
* in case of any errors.
|
||||
* Validate the given method arguments and return the result of validation.
|
||||
* @param target the target Object
|
||||
* @param method the target method
|
||||
* @param arguments candidate arguments for a method invocation
|
||||
* @param groups groups for validation determined via
|
||||
* {@link #determineValidationGroups(Object, Method)}
|
||||
* @return a result with {@link ConstraintViolation violations} and
|
||||
* {@link ParameterValidationResult validationResults}, both possibly empty
|
||||
* in case there are no violations
|
||||
*/
|
||||
public void validateMethodArguments(Object target, Method method, Object[] arguments, Class<?>[] groups) {
|
||||
public MethodValidationResult validateMethodArguments(
|
||||
Object target, Method method, Object[] arguments, Class<?>[] groups) {
|
||||
|
||||
ExecutableValidator execVal = this.validator.get().forExecutables();
|
||||
Set<ConstraintViolation<Object>> result;
|
||||
try {
|
||||
@@ -207,28 +214,26 @@ public class MethodValidationAdapter {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(mostSpecificMethod);
|
||||
result = execVal.validateParameters(target, bridgedMethod, arguments, groups);
|
||||
}
|
||||
if (!result.isEmpty()) {
|
||||
throw createException(target, method, result, i -> arguments[i]);
|
||||
}
|
||||
return (result.isEmpty() ? EMPTY_RESULT : createException(target, method, result, i -> arguments[i]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given return value and raise {@link ConstraintViolation}
|
||||
* in case of any errors.
|
||||
* Validate the given return value and return the result of validation.
|
||||
* @param target the target Object
|
||||
* @param method the target method
|
||||
* @param returnValue value returned from invoking the target method
|
||||
* @param groups groups for validation determined via
|
||||
* {@link #determineValidationGroups(Object, Method)}
|
||||
* @return a result with {@link ConstraintViolation violations} and
|
||||
* {@link ParameterValidationResult validationResults}, both possibly empty
|
||||
* in case there are no violations
|
||||
*/
|
||||
public void validateMethodReturnValue(
|
||||
public MethodValidationResult validateMethodReturnValue(
|
||||
Object target, Method method, @Nullable Object returnValue, Class<?>[] groups) {
|
||||
|
||||
ExecutableValidator execVal = this.validator.get().forExecutables();
|
||||
Set<ConstraintViolation<Object>> result = execVal.validateReturnValue(target, method, returnValue, groups);
|
||||
if (!result.isEmpty()) {
|
||||
throw createException(target, method, result, i -> returnValue);
|
||||
}
|
||||
return (result.isEmpty() ? EMPTY_RESULT : createException(target, method, result, i -> returnValue));
|
||||
}
|
||||
|
||||
private MethodValidationException createException(
|
||||
@@ -275,7 +280,7 @@ public class MethodValidationAdapter {
|
||||
cascadedViolations.forEach((node, builder) -> validatonResultList.add(builder.build()));
|
||||
validatonResultList.sort(RESULT_COMPARATOR);
|
||||
|
||||
return new MethodValidationException(target, method, validatonResultList, violations);
|
||||
return new MethodValidationException(target, method, violations, validatonResultList);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -470,4 +475,36 @@ public class MethodValidationAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link MethodValidationResult} to use when there are no violations.
|
||||
*/
|
||||
private static final class EmptyMethodValidationResult implements MethodValidationResult {
|
||||
|
||||
@Override
|
||||
public Set<ConstraintViolation<?>> getConstraintViolations() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ParameterValidationResult> getAllValidationResults() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ParameterValidationResult> getValueResults() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ParameterErrors> getBeanResults() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void throwIfViolationsPresent() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,17 +23,15 @@ import java.util.Set;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Extension of {@link ConstraintViolationException} that exposes an additional
|
||||
* list of {@link ParameterValidationResult} with violations adapted to
|
||||
* Extension of {@link ConstraintViolationException} that implements
|
||||
* {@link MethodValidationResult} exposing an additional list of
|
||||
* {@link ParameterValidationResult} that represents violations adapted to
|
||||
* {@link org.springframework.context.MessageSourceResolvable} and grouped by
|
||||
* method parameter.
|
||||
*
|
||||
* <p>For {@link jakarta.validation.Valid @Valid}-annotated, Object method
|
||||
* parameters or return types with cascaded violations, the {@link ParameterErrors}
|
||||
* subclass of {@link ParameterValidationResult} implements
|
||||
* {@link org.springframework.validation.Errors} and exposes
|
||||
* {@link org.springframework.validation.FieldError field errors}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 6.1
|
||||
@@ -42,7 +40,7 @@ import jakarta.validation.ConstraintViolationException;
|
||||
* @see MethodValidationAdapter
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MethodValidationException extends ConstraintViolationException {
|
||||
public class MethodValidationException extends ConstraintViolationException implements MethodValidationResult {
|
||||
|
||||
private final Object target;
|
||||
|
||||
@@ -52,11 +50,11 @@ public class MethodValidationException extends ConstraintViolationException {
|
||||
|
||||
|
||||
public MethodValidationException(
|
||||
Object target, Method method,
|
||||
List<ParameterValidationResult> validationResults,
|
||||
Set<? extends ConstraintViolation<?>> violations) {
|
||||
Object target, Method method, Set<? extends ConstraintViolation<?>> violations,
|
||||
List<ParameterValidationResult> validationResults) {
|
||||
|
||||
super(violations);
|
||||
Assert.notEmpty(violations, "'violations' must not be empty");
|
||||
this.target = target;
|
||||
this.method = method;
|
||||
this.allValidationResults = validationResults;
|
||||
@@ -77,37 +75,26 @@ public class MethodValidationException extends ConstraintViolationException {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all validation results. This includes method parameters with
|
||||
* constraints declared on them, as well as
|
||||
* {@link jakarta.validation.Valid @Valid} method parameters with
|
||||
* cascaded constraints.
|
||||
* @see #getValueResults()
|
||||
* @see #getBeanResults()
|
||||
*/
|
||||
// re-declare parent class method for NonNull treatment of interface
|
||||
|
||||
@Override
|
||||
public Set<ConstraintViolation<?>> getConstraintViolations() {
|
||||
return super.getConstraintViolations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ParameterValidationResult> getAllValidationResults() {
|
||||
return this.allValidationResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only validation results for method parameters with constraints
|
||||
* declared directly on them. This excludes
|
||||
* {@link jakarta.validation.Valid @Valid} method parameters with cascaded
|
||||
* constraints.
|
||||
* @see #getAllValidationResults()
|
||||
*/
|
||||
@Override
|
||||
public List<ParameterValidationResult> getValueResults() {
|
||||
return this.allValidationResults.stream()
|
||||
.filter(result -> !(result instanceof ParameterErrors))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only validation results for {@link jakarta.validation.Valid @Valid}
|
||||
* method parameters with cascaded constraints. This excludes method
|
||||
* parameters with constraints declared directly on them.
|
||||
* @see #getAllValidationResults()
|
||||
*/
|
||||
@Override
|
||||
public List<ParameterErrors> getBeanResults() {
|
||||
return this.allValidationResults.stream()
|
||||
.filter(result -> result instanceof ParameterErrors)
|
||||
@@ -115,4 +102,9 @@ public class MethodValidationException extends ConstraintViolationException {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void throwIfViolationsPresent() {
|
||||
throw this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -103,11 +103,15 @@ public class MethodValidationInterceptor implements MethodInterceptor {
|
||||
Object target = getTarget(invocation);
|
||||
Method method = invocation.getMethod();
|
||||
Class<?>[] groups = determineValidationGroups(invocation);
|
||||
this.delegate.validateMethodArguments(target, method, invocation.getArguments(), groups);
|
||||
|
||||
this.delegate.validateMethodArguments(target, method, invocation.getArguments(), groups)
|
||||
.throwIfViolationsPresent();
|
||||
|
||||
Object returnValue = invocation.proceed();
|
||||
|
||||
this.delegate.validateMethodReturnValue(target, method, returnValue, groups);
|
||||
this.delegate.validateMethodReturnValue(target, method, returnValue, groups)
|
||||
.throwIfViolationsPresent();
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
|
||||
/**
|
||||
* Container for method validation results where underlying
|
||||
* {@link ConstraintViolation violations} have been adapted to
|
||||
* {@link ParameterValidationResult} each containing a list of
|
||||
* {@link org.springframework.context.MessageSourceResolvable} grouped by method
|
||||
* parameter.
|
||||
*
|
||||
* <p>For {@link jakarta.validation.Valid @Valid}-annotated, Object method
|
||||
* parameters or return types with cascaded violations, the {@link ParameterErrors}
|
||||
* subclass of {@link ParameterValidationResult} implements
|
||||
* {@link org.springframework.validation.Errors} and exposes
|
||||
* {@link org.springframework.validation.FieldError field errors}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 6.1
|
||||
*/
|
||||
public interface MethodValidationResult {
|
||||
|
||||
/**
|
||||
* Returns the set of constraint violations reported during a validation.
|
||||
* @return the {@code Set} of {@link ConstraintViolation}s, or an empty Set
|
||||
*/
|
||||
Set<ConstraintViolation<?>> getConstraintViolations();
|
||||
|
||||
/**
|
||||
* Return all validation results. This includes method parameters with
|
||||
* constraints declared on them, as well as
|
||||
* {@link jakarta.validation.Valid @Valid} method parameters with
|
||||
* cascaded constraints.
|
||||
* @see #getValueResults()
|
||||
* @see #getBeanResults()
|
||||
*/
|
||||
List<ParameterValidationResult> getAllValidationResults();
|
||||
|
||||
/**
|
||||
* Return only validation results for method parameters with constraints
|
||||
* declared directly on them. This excludes
|
||||
* {@link jakarta.validation.Valid @Valid} method parameters with cascaded
|
||||
* constraints.
|
||||
* @see #getAllValidationResults()
|
||||
*/
|
||||
List<ParameterValidationResult> getValueResults();
|
||||
|
||||
/**
|
||||
* Return only validation results for {@link jakarta.validation.Valid @Valid}
|
||||
* method parameters with cascaded constraints. This excludes method
|
||||
* parameters with constraints declared directly on them.
|
||||
* @see #getAllValidationResults()
|
||||
*/
|
||||
List<ParameterErrors> getBeanResults();
|
||||
|
||||
/**
|
||||
* Check if {@link #getConstraintViolations()} is empty, and if not, raise
|
||||
* {@link MethodValidationException}.
|
||||
* @throws MethodValidationException if the result contains any violations
|
||||
*/
|
||||
void throwIfViolationsPresent();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user