mirror of https://github.com/halo-dev/halo
Add iterable objects validation (#970)
parent
83d4ca1565
commit
7a71c85862
|
@ -1,15 +1,14 @@
|
||||||
package run.halo.app.utils;
|
package run.halo.app.utils;
|
||||||
|
|
||||||
|
import org.hibernate.validator.internal.engine.path.PathImpl;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.validation.FieldError;
|
import org.springframework.validation.FieldError;
|
||||||
|
|
||||||
import javax.validation.ConstraintViolation;
|
import javax.validation.*;
|
||||||
import javax.validation.ConstraintViolationException;
|
|
||||||
import javax.validation.Validation;
|
|
||||||
import javax.validation.Validator;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object validation utilities.
|
* Object validation utilities.
|
||||||
|
@ -30,7 +29,7 @@ public class ValidationUtils {
|
||||||
* @return validator
|
* @return validator
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public static Validator getValidatorOrCreate() {
|
public static Validator getValidator() {
|
||||||
if (VALIDATOR == null) {
|
if (VALIDATOR == null) {
|
||||||
synchronized (ValidationUtils.class) {
|
synchronized (ValidationUtils.class) {
|
||||||
if (VALIDATOR == null) {
|
if (VALIDATOR == null) {
|
||||||
|
@ -52,9 +51,13 @@ public class ValidationUtils {
|
||||||
*/
|
*/
|
||||||
public static void validate(Object obj, Class<?>... groups) {
|
public static void validate(Object obj, Class<?>... groups) {
|
||||||
|
|
||||||
Validator validator = getValidatorOrCreate();
|
Validator validator = getValidator();
|
||||||
|
|
||||||
// Validate the object
|
if (obj instanceof Iterable) {
|
||||||
|
// validate for iterable
|
||||||
|
validate((Iterable<?>) obj, groups);
|
||||||
|
} else {
|
||||||
|
// validate the non-iterable object
|
||||||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(obj, groups);
|
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(obj, groups);
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(constraintViolations)) {
|
if (!CollectionUtils.isEmpty(constraintViolations)) {
|
||||||
|
@ -62,7 +65,41 @@ public class ValidationUtils {
|
||||||
throw new ConstraintViolationException(constraintViolations);
|
throw new ConstraintViolationException(constraintViolations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates iterable objects.
|
||||||
|
*
|
||||||
|
* @param objs iterable objects could be null
|
||||||
|
* @param groups validation groups
|
||||||
|
*/
|
||||||
|
public static void validate(@Nullable Iterable<?> objs, @Nullable Class<?>... groups) {
|
||||||
|
if (objs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get validator
|
||||||
|
Validator validator = getValidator();
|
||||||
|
|
||||||
|
// wrap index
|
||||||
|
AtomicInteger i = new AtomicInteger(0);
|
||||||
|
final Set<ConstraintViolation<?>> allViolations = new LinkedHashSet<>();
|
||||||
|
objs.forEach(obj -> {
|
||||||
|
int index = i.getAndIncrement();
|
||||||
|
Set<? extends ConstraintViolation<?>> violations = validator.validate(obj, groups);
|
||||||
|
violations.forEach(violation -> {
|
||||||
|
Path path = violation.getPropertyPath();
|
||||||
|
if (path instanceof PathImpl) {
|
||||||
|
PathImpl pathImpl = (PathImpl) path;
|
||||||
|
pathImpl.makeLeafNodeIterableAndSetIndex(index);
|
||||||
|
}
|
||||||
|
allViolations.add(violation);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (!CollectionUtils.isEmpty(allViolations)) {
|
||||||
|
throw new ConstraintViolationException(allViolations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将字段验证错误转换为标准的map型,key:value = field:message
|
* 将字段验证错误转换为标准的map型,key:value = field:message
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
package run.halo.app.utils;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.ConstraintViolationException;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validation utils test.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
class ValidationUtilsTest {
|
||||||
|
|
||||||
|
Validator validator = ValidationUtils.getValidator();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateObjectTest() {
|
||||||
|
Car car = new Car(null);
|
||||||
|
Set<ConstraintViolation<Car>> violations = validator.validate(car);
|
||||||
|
validateObjectAssert(violations);
|
||||||
|
|
||||||
|
ConstraintViolationException exception = assertThrows(ConstraintViolationException.class,
|
||||||
|
() -> ValidationUtils.validate(car));
|
||||||
|
validateObjectAssert(exception.getConstraintViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
void validateObjectAssert(Set<? extends ConstraintViolation<?>> violations) {
|
||||||
|
assertEquals(1, violations.size());
|
||||||
|
ConstraintViolation<?> violation = violations.iterator().next();
|
||||||
|
assertEquals("name", violation.getPropertyPath().toString());
|
||||||
|
assertEquals("Name must not be blank", violation.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateListTest() {
|
||||||
|
List<Car> cars = Arrays.asList(new Car(""),
|
||||||
|
new Car("car name"),
|
||||||
|
new Car(null));
|
||||||
|
|
||||||
|
ConstraintViolationException exception = assertThrows(ConstraintViolationException.class,
|
||||||
|
() -> ValidationUtils.validate(cars));
|
||||||
|
|
||||||
|
validateIteratorTest(exception.getConstraintViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
void validateIteratorTest(Set<? extends ConstraintViolation<?>> violations) {
|
||||||
|
assertEquals(2, violations.size());
|
||||||
|
|
||||||
|
LinkedList<? extends ConstraintViolation<?>> violationList = new LinkedList<>(violations);
|
||||||
|
violationList.sort(Comparator.comparing(v -> v.getPropertyPath().toString()));
|
||||||
|
|
||||||
|
// get first violation
|
||||||
|
ConstraintViolation<?> firstViolation = violationList.get(0);
|
||||||
|
// get second violation
|
||||||
|
ConstraintViolation<?> secondViolation = violationList.get(1);
|
||||||
|
|
||||||
|
assertEquals("name[0]", firstViolation.getPropertyPath().toString());
|
||||||
|
assertEquals("name[2]", secondViolation.getPropertyPath().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Car entity.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
static class Car {
|
||||||
|
|
||||||
|
@NotBlank(message = "Name must not be blank")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue