Compare commits
3 Commits
2d769fde26
...
2ac8e39387
Author | SHA1 | Date | |
---|---|---|---|
2ac8e39387 | |||
3b9a224e72 | |||
44ea11e0e9 |
105
README.md
Normal file
105
README.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Plusone Validator
|
||||
|
||||
## 简介
|
||||
Plusone Validator 是一个校验库,使用 lambda 表达式(包括方法引用)和流式 API 构建校验规则,对对象进行校验。
|
||||
|
||||
## 安装
|
||||
Plusone Validator 暂未提交 maven 中央仓库,可 clone 代码仓库,通过 maven 安装到本地库,然后在项目中引入。
|
||||
|
||||
## 示例
|
||||
|
||||
### 校验对象
|
||||
|
||||
定义校验器:
|
||||
```java
|
||||
class CustomerValidator extends BaseValidator<Customer> {
|
||||
|
||||
private static final CustomerValidator INSTANCE = new CustomerValidator();
|
||||
|
||||
private CustomerValidator() {
|
||||
ruleFor(Customer::getName).notBlank("姓名不能为空");
|
||||
ruleFor(Customer::getEmailAddress).notBlank().emailAddress();
|
||||
ruleFor(Customer::getVipLevel).notNull().inRange(Range.closed(0, 10), "会员等级必须在0-10之间");
|
||||
ruleFor(Customer::getCustomerId).notBlank("客户编号不能为空");
|
||||
ruleFor(Customer::getBirthday)
|
||||
.notNull("生日不能为空")
|
||||
.must(LocalDate.now().minusYears(16)::isAfter, "用户必须大于16周岁");
|
||||
ruleFor(Customer::getAddress).length(20, 250, "地址长度必须在20-250之间");
|
||||
ruleFor((Customer customer) -> Pair.of(customer.getVipLevel(), customer.getBirthday()))
|
||||
.must(CustomerValidator::validateAge, "5级以上会员必须满18周岁");
|
||||
}
|
||||
|
||||
private static boolean validateAge(Pair<Integer, LocalDate> vipLevelAndBirthday) {
|
||||
Integer vipLevel = vipLevelAndBirthday.getLeft();
|
||||
LocalDate birthday = vipLevelAndBirthday.getRight();
|
||||
return vipLevel <= 5 || LocalDate.now().minusYears(18).isAfter(birthday);
|
||||
}
|
||||
|
||||
public static CustomerValidator getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使用:
|
||||
```java
|
||||
public void foo(Customer customer) {
|
||||
CustomerValidator.getInstance().validate(customer);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 校验 Map
|
||||
|
||||
定义校验器:
|
||||
```java
|
||||
class CustomerMapValidator extends MapValidator<String, Object> {
|
||||
|
||||
private static final CustomerMapValidator INSTANCE = new CustomerMapValidator();
|
||||
|
||||
private static final String[] FIELD_NAMES = {
|
||||
"name", "emailAddress", "vipLevel", "customerId", "birthday", "address"
|
||||
};
|
||||
|
||||
private CustomerMapValidator() {
|
||||
super(FIELD_NAMES);
|
||||
|
||||
ruleForString("name").notBlank("姓名不能为空");
|
||||
ruleForString("emailAddress").notBlank().emailAddress();
|
||||
ruleForInt("vipLevel").notNull().inRange(Range.closed(0, 10), "会员等级必须在0-10之间");
|
||||
ruleForString("customerId").notBlank("客户编号不能为空");
|
||||
this.<LocalDate>ruleFor("birthday")
|
||||
.notNull("生日不能为空")
|
||||
.must(LocalDate.now().minusYears(16)::isAfter, "用户必须大于16周岁");
|
||||
ruleForString("address").length(20, 250, "地址长度必须在20-250之间");
|
||||
this.<Pair<Integer, LocalDate>>ruleFor((Map<String, Object> customer) ->
|
||||
Pair.of(MapUtils.getInteger(customer, "vipLevel"), (LocalDate) customer.get("birthday")))
|
||||
.must(CustomerMapValidator::validateAge, "5级以上会员必须满18周岁");
|
||||
}
|
||||
|
||||
private static boolean validateAge(Pair<Integer, LocalDate> vipLevelAndBirthday) {
|
||||
Integer vipLevel = vipLevelAndBirthday.getLeft();
|
||||
LocalDate birthday = vipLevelAndBirthday.getRight();
|
||||
return vipLevel <= 5 || LocalDate.now().minusYears(18).isAfter(birthday);
|
||||
}
|
||||
|
||||
public static CustomerMapValidator getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使用:
|
||||
```java
|
||||
public void foo(Map<String, Object> customer) {
|
||||
CustomerMapValidator.getInstance().validate(customer);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
## 其他
|
||||
|
||||
Plusone Validator 是个人在学习 Microsoft 的 [eShop](https://github.com/dotnet/eShop) 时,被其中 [FluentValidation](https://github.com/FluentValidation/FluentValidation) 的 API 所吸引,出于学习和个人使用的目的进行开发和维护。使用 Apache License 2.0 开源。
|
||||
|
||||
欢迎通过 issue 反馈使用过程中发现的问题和建议。
|
@@ -73,7 +73,7 @@ public abstract class BaseComparablePropertyValidator<T, TProperty extends Compa
|
||||
*/
|
||||
public <E extends RuntimeException> TPropertyValidator inRange(
|
||||
Range<TProperty> range, Supplier<E> e) {
|
||||
withRule(value -> value != null && range.contains(value), e);
|
||||
withRule(value -> value != null && range.contains(value), convertToExceptionFunction(e));
|
||||
return thisObject();
|
||||
}
|
||||
|
||||
|
@@ -44,28 +44,6 @@ public abstract class BasePropertyValidator<T, TProperty, TPropertyValidator ext
|
||||
this.getter = getter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条校验属性的规则
|
||||
*
|
||||
* @param rule 校验规则
|
||||
* @return 属性校验器
|
||||
*/
|
||||
public final TPropertyValidator withRule(Predicate<? super TProperty> rule) {
|
||||
return withRule(rule, v -> ValidationException.withDefaultMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条校验属性的规则
|
||||
*
|
||||
* @param rule 校验规则
|
||||
* @param errMsg 校验失败的错误信息
|
||||
* @return 属性校验器
|
||||
*/
|
||||
public final TPropertyValidator withRule(
|
||||
Predicate<? super TProperty> rule, String errMsg) {
|
||||
return withRule(rule, convertToExceptionFunction(errMsg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条校验属性的规则
|
||||
*
|
||||
@@ -73,19 +51,7 @@ public abstract class BasePropertyValidator<T, TProperty, TPropertyValidator ext
|
||||
* @param e 自定义异常
|
||||
* @return 属性校验器
|
||||
*/
|
||||
public final <E extends RuntimeException> TPropertyValidator withRule(
|
||||
Predicate<? super TProperty> rule, Supplier<E> e) {
|
||||
return withRule(rule, convertToExceptionFunction(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条校验属性的规则
|
||||
*
|
||||
* @param rule 校验规则
|
||||
* @param e 自定义异常
|
||||
* @return 属性校验器
|
||||
*/
|
||||
public final <E extends RuntimeException> TPropertyValidator withRule(
|
||||
protected final <E extends RuntimeException> TPropertyValidator withRule(
|
||||
Predicate<? super TProperty> rule, Function<TProperty, E> e) {
|
||||
this.consumers.add(v -> {
|
||||
if (!rule.test(v)) {
|
||||
|
@@ -110,9 +110,10 @@ public abstract class MapValidator<K, V> extends BaseValidator<Map<K, V>> {
|
||||
* @param key key
|
||||
* @return 属性校验器
|
||||
*/
|
||||
protected final ObjectPropertyValidator<Map<K, V>, V> ruleFor(K key) {
|
||||
final Function<Map<K, V>, V> func = m -> m.get(key);
|
||||
return ruleFor(func);
|
||||
protected final <T extends V> ObjectPropertyValidator<Map<K, V>, T> ruleFor(K key) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Function<Map<K, V>, T> func = m -> (T) m.get(key);
|
||||
return super.<T>ruleFor(func);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -20,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -30,8 +29,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import xyz.zhouxy.plusone.ExampleException;
|
||||
import xyz.zhouxy.plusone.commons.collection.CollectionTools;
|
||||
import xyz.zhouxy.plusone.commons.util.DateTimeTools;
|
||||
import xyz.zhouxy.plusone.commons.util.StringTools;
|
||||
import xyz.zhouxy.plusone.example.ExampleCommand;
|
||||
import xyz.zhouxy.plusone.example.Foo;
|
||||
@@ -41,114 +38,6 @@ import xyz.zhouxy.plusone.validator.ValidationException;
|
||||
|
||||
public class ObjectPropertyValidatorTests {
|
||||
|
||||
// ================================
|
||||
// #region - withRule
|
||||
// ================================
|
||||
|
||||
@Test
|
||||
void withRule_validInput() {
|
||||
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleFor(ExampleCommand::getBoolProperty)
|
||||
.notNull("The boolProperty cannot be null.")
|
||||
.withRule(Boolean.TRUE::equals);
|
||||
|
||||
ruleFor(ExampleCommand::getIntProperty)
|
||||
.withRule(intProperty -> intProperty > 0, "The intProperty should be greater than 0.");
|
||||
|
||||
ruleFor(ExampleCommand::getLongProperty)
|
||||
.withRule(longProperty -> longProperty > 0L,
|
||||
() -> ExampleException.withMessage("The longProperty should be greater than 0."));
|
||||
|
||||
ruleFor(ExampleCommand::getDoubleProperty)
|
||||
.withRule(doubleProperty -> doubleProperty > 0.00,
|
||||
doubleProperty -> ExampleException.withMessage("The doubleProperty should be greater than 0, but it was: %s", doubleProperty));
|
||||
|
||||
ruleFor(ExampleCommand::getStringProperty)
|
||||
.notNull()
|
||||
.withRule(stringProperty -> stringProperty.length() > 2,
|
||||
() -> ExampleException.withMessage("The length of stringProperty should be greater than 2."));
|
||||
|
||||
ruleFor(ExampleCommand::getDateTimeProperty)
|
||||
.withRule(DateTimeTools::isFuture,
|
||||
() -> new DateTimeException("The dateTimeProperty should be a future time."));
|
||||
|
||||
ruleFor(ExampleCommand::getObjectProperty)
|
||||
.notNull("The objectProperty cannot be null.");
|
||||
|
||||
ruleFor(ExampleCommand::getStringListProperty)
|
||||
.withRule(CollectionTools::isNotEmpty, "The stringListProperty cannot be empty.");
|
||||
|
||||
withRule(command -> {
|
||||
Foo objectProperty = command.getObjectProperty();
|
||||
if (!Objects.equals(command.getIntProperty(), objectProperty.getIntProperty())) {
|
||||
throw ExampleException.withMessage("intProperty invalid.");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ExampleCommand command = new ExampleCommand(
|
||||
true,
|
||||
Integer.MAX_VALUE,
|
||||
Long.MAX_VALUE,
|
||||
Double.MAX_VALUE,
|
||||
"StringValue",
|
||||
LocalDateTime.now().plusDays(1),
|
||||
new Foo(Integer.MAX_VALUE, "StringValue"),
|
||||
Lists.newArrayList("ABC", "DEF"));
|
||||
|
||||
assertDoesNotThrow(() -> validator.validate(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRule_invalidInputs() {
|
||||
ExampleCommand command = new ExampleCommand();
|
||||
IValidator<ExampleCommand> ruleWithDefaultMessage = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleFor(ExampleCommand::getObjectProperty)
|
||||
.withRule(x -> false);
|
||||
}
|
||||
};
|
||||
ValidationException eWithDefaultMessage = assertThrows(
|
||||
ValidationException.class, () -> ruleWithDefaultMessage.validate(command));
|
||||
assertEquals(ValidationException.DEFAULT_MESSAGE, eWithDefaultMessage.getMessage());
|
||||
|
||||
IValidator<ExampleCommand> ruleWithMessage = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleFor(ExampleCommand::getObjectProperty)
|
||||
.withRule(x -> false, "invalid input.");
|
||||
}
|
||||
};
|
||||
ValidationException eWithMessage = assertThrows(
|
||||
ValidationException.class, () -> ruleWithMessage.validate(command));
|
||||
assertEquals("invalid input.", eWithMessage.getMessage());
|
||||
|
||||
IValidator<ExampleCommand> ruleWithExceptionSupplier = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleFor(ExampleCommand::getObjectProperty)
|
||||
.withRule(x -> false, () -> ExampleException.withMessage("invalid input."));
|
||||
}
|
||||
};
|
||||
ExampleException specifiedException = assertThrows(
|
||||
ExampleException.class, () -> ruleWithExceptionSupplier.validate(command));
|
||||
assertEquals("invalid input.", specifiedException.getMessage());
|
||||
|
||||
IValidator<ExampleCommand> ruleWithExceptionFunction = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleFor(ExampleCommand::getObjectProperty)
|
||||
.withRule(x -> false, x -> ExampleException.withMessage("invalid input: [%s].", x));
|
||||
}
|
||||
};
|
||||
ExampleException specifiedException2 = assertThrows(
|
||||
ExampleException.class, () -> ruleWithExceptionFunction.validate(command));
|
||||
assertEquals("invalid input: [null].", specifiedException2.getMessage());
|
||||
}
|
||||
|
||||
// ================================
|
||||
// #endregion - withRule
|
||||
// ================================
|
||||
|
||||
// ================================
|
||||
// #region - notNull
|
||||
// ================================
|
||||
|
Reference in New Issue
Block a user