简化验证
一个受 FP 启发的验证 DSL。适用于 Dart 和 Flutter 项目。
特点
- 完全可扩展(创建您自己的组合器、验证器原语等)
- Flexible Verify 是一个基于扩展的 API(没有创建单个类,全是纯函数)
- 可定制(如果需要,定义您自己的错误类型)以任何您想要的方式组织验证器
- Bloc 友好(请参阅 示例 以获得具体的实现)
- Null 安全(预览版)
用法
创建验证器
验证器只是一个简单的函数别名
// One of 2 variats depending on wether the validated subject will be transformed into another type or not
typedef ValidatorT<S, T> = Either<List<dynamic>, T> Function(S subject);
typedef Validator<S> = Either<List<dynamic>, S> Function(S subject);
因此,您可以通过仅指定一个函数来创建自己的验证器,例如
final Validator<String> emailValidator = (String email) {
return email.contains('@') ? Right(email) : Left('Bad email format')
};
从谓词创建简单的验证器
更简单的方法是使用一些内置的辅助函数。
final contains@ = Verify.that(
(String email) => email.contains('@'),
error: 'Bad email format'
);
使用自定义错误并通过类型进行过滤
重用验证器
使用组合来构建更复杂的验证器。
final Validator<String> emailValidator = Verify.all([ contains@, notEmpty ])
验证和转换
验证器也能够转换它们的输入,所以例如我们可以这样做
一次性完成解析和验证。
final Validator<String, int> intParsingValidator = (String str) => Right(int.parse(str));
final validator = intParsingValidator.onException((_) => Error('not an integer'));
字段验证
给定一个模型,例如一个用户
class User extends Equatable {
final String? phone;
final String? mail;
final int? age;
const User(this.phone, this.mail, this.age);
@override
List<Object> get props => [phone ?? '', mail ?? '', age ?? ''];
}
可以通过链接一系列 check
和 checkField 方法对对象及其字段执行额外的检查。
final userValidator = Verify.empty<User>()
.check((user) => !user.phone!.isEmpty, error: Error('phone empty'))
.checkField((user) => user.mail!, emailValidator);
final someUser = User('','', 25);
final Either<List<Error>, User> validationResult = userValidator.verify(someUser);
注意:check 和 checkField 的区别在于后者会在值为空时忽略验证,
这在支持 Null 安全的下一版本中可能会改变。
运行验证器
运行验证器就像传递一个参数一样简单,因为它只是一个函数。
为了更清晰地说明,提供了一个 verify 方法,这个方法很特别,因为它除了
将参数转发给调用验证器之外,还可以用于过滤错误列表并
将其转换为特定错误类型。只需提供一个特定的类型参数。
final signUpValidation = Verify.subject<SignUpState>();
final errors = signUpValidation
.verify<SignUpError>(newState); // Either<List<SignUpError>, SignUpState>
内置验证器
Verify 没有自带很多内置验证器,因为它们非常容易创建。
它确实带有一些正则表达式的简写。
final validator = Verify.fromRegex(RegExp(r"(^\d+$)", error: Error('not just digits')) // Validator<String, int>
表单验证
通常您会为您的错误类型建模类似于
enum FormField {
email,
password,
passwordConfirmation,
}
class SignUpError extends ValidationError {
final String message;
final FormField field;
SignUpError(this.message, {required this.field});
@override
String get errorDescription => message;
}
在这些情况下,能够按字段对错误进行分组会很方便。
Verify 为此提供的解决方案是
final validator = Verify.inOrder<SignUpFormState>([
validateMail,
validatePassword,
validateConfirmation,
]);
final Map<FormField, SignUpError> errorMap = validator
.verify<SignUpError>(someState)
.groupedErrorsBy((error) => error.field);
顺序执行
可以使用一个略有不同的 API 来实现与 inOrder 组合函数相同的结果。
final numberValidator = Verify.subject<int>()
.then(Verify.that(
(subject) => subject % 2 == 0,
error: Error('not even'),
))
.then(Verify.that(
(subject) => subject >= 10,
error: Error('single digit'),
));
final errors2 = numberValidator.errors(3); // yields 1 error
final errors = numberValidator.errors(4); // yields 1 error
这样您就可以快速访问按字段分段的错误。