Clean Architecture 中的表单验证
Form validation in Clean Architecture
输入验证是一种业务逻辑,因此我们应该将此过程隐藏在领域层中。
正如所讨论的 here
我是这样做的
登录验证器
class LoginValidator extends Validator {
String email;
String password;
LoginValidator(this.email, this.password);
@override
void validate(Function() success, Function(List<Failure>) errors) {
List<Failure> failures = [];
if (email.trim().isEmpty) {
failures.add(const EmailValidationFailure('Email is required'));
} else if (!validator.isEmail(email)) {
failures.add(const EmailValidationFailure());
}
if (password.trim().isEmpty) {
failures.add(const PasswordValidationFailure('Password is required'));
}
if (failures.isNotEmpty) {
errors(failures);
} else {
success();
}
}
}
并且我为每个输入字段创建了一个失败 class
class EmailValidationFailure extends Failure {
const EmailValidationFailure([String message = 'Email is invalid'])
: super(message);
}
class PasswordValidationFailure extends Failure {
const PasswordValidationFailure([String message = 'Incorrect password'])
: super(message);
}
并且我在用例中使用验证器
class LoginUseCaseInteractor implements LoginUseCaseInputPort {
final AccountRepository _repository;
final LoginUseCaseOutputPort outputPort;
LoginUseCaseInteractor(this._repository, this.outputPort);
@override
void login(LoginParams params) {
LoginValidator(params.email, params.password).validate(() async {
outputPort.loading();
Result<bool> result = await _repository.login(params);
result.when(
success: (data) {
outputPort.success();
},
error: (error) {
outputPort.requestError(error);
},
);
}, (errors) {
outputPort.formValidationErrors(errors);
});
}
}
最后我在演示器中处理演示逻辑,它实现了登录用例的输出端口
class LoginPresenter implements LoginUseCaseOutputPort {
Reader read;
LoginPresenter(this.read) : super(LoginState.initial());
@override
void formValidationErrors(List<Failure> errors) =>
state = LoginState.formValidationErrors(errors);
@override
void success() => read(setRootPresenterProvider.notifier).setMainPageAsRoot();
@override
void loading() => state = LoginState.loading();
@override
void requestError(Failure error) => state = LoginState.requestError(error);
}
我做什么
我为每个输入字段创建一个失败 class 和列表中所有字段的 return 个失败,在表示逻辑中我按类型检查输入失败
我的问题:
如果我有一个大表格(例如:15 个字段)怎么办,我应该为每个字段创建一个失败 class 吗?
有没有更好的方法来处理验证?
My Question: What if I have a large form (eg:15 fields), Should I create a failure class for each of them? Is there a better way to handle the validation?
每个可能发生的错误都必须是可识别的。通过专用 classes、故障代码或常量。您选择哪一个取决于您要提供给客户端的错误上下文信息。
我也不会将消息放在失败对象中,因为由演示者来选择错误类型的演示。向用户显示错误的方式在很大程度上取决于用户界面。也许错误不需要字符串,它只是以图标或彩色标记等形式呈现。交互器不应创建特定于语言的字符串。这是用户界面的一部分。
在简单的情况下,您可能只需要一个错误代码或常量,如 401
,演示者将其转换为 Login failed
。当然,对于这种情况,您也可以使用通用错误对象。
class失败{
内部代码;
}
在其他情况下,您可能希望显示更详细的错误消息,例如 E-mails under the domain '@somedomain.com' are not allowed to login.
。在这种情况下,您应该使用错误对象而不是仅使用简单的代码来提供详细信息。例如
class 登录域失败 {
字符串本地部分;
字符串域名
}
演示者然后可以使用此信息生成失败字符串或以其他方式显示错误,例如在输入字段中突出显示域名或您能想到的任何内容。
输入验证是一种业务逻辑,因此我们应该将此过程隐藏在领域层中。 正如所讨论的 here
我是这样做的
登录验证器
class LoginValidator extends Validator {
String email;
String password;
LoginValidator(this.email, this.password);
@override
void validate(Function() success, Function(List<Failure>) errors) {
List<Failure> failures = [];
if (email.trim().isEmpty) {
failures.add(const EmailValidationFailure('Email is required'));
} else if (!validator.isEmail(email)) {
failures.add(const EmailValidationFailure());
}
if (password.trim().isEmpty) {
failures.add(const PasswordValidationFailure('Password is required'));
}
if (failures.isNotEmpty) {
errors(failures);
} else {
success();
}
}
}
并且我为每个输入字段创建了一个失败 class
class EmailValidationFailure extends Failure {
const EmailValidationFailure([String message = 'Email is invalid'])
: super(message);
}
class PasswordValidationFailure extends Failure {
const PasswordValidationFailure([String message = 'Incorrect password'])
: super(message);
}
并且我在用例中使用验证器
class LoginUseCaseInteractor implements LoginUseCaseInputPort {
final AccountRepository _repository;
final LoginUseCaseOutputPort outputPort;
LoginUseCaseInteractor(this._repository, this.outputPort);
@override
void login(LoginParams params) {
LoginValidator(params.email, params.password).validate(() async {
outputPort.loading();
Result<bool> result = await _repository.login(params);
result.when(
success: (data) {
outputPort.success();
},
error: (error) {
outputPort.requestError(error);
},
);
}, (errors) {
outputPort.formValidationErrors(errors);
});
}
}
最后我在演示器中处理演示逻辑,它实现了登录用例的输出端口
class LoginPresenter implements LoginUseCaseOutputPort {
Reader read;
LoginPresenter(this.read) : super(LoginState.initial());
@override
void formValidationErrors(List<Failure> errors) =>
state = LoginState.formValidationErrors(errors);
@override
void success() => read(setRootPresenterProvider.notifier).setMainPageAsRoot();
@override
void loading() => state = LoginState.loading();
@override
void requestError(Failure error) => state = LoginState.requestError(error);
}
我做什么 我为每个输入字段创建一个失败 class 和列表中所有字段的 return 个失败,在表示逻辑中我按类型检查输入失败
我的问题: 如果我有一个大表格(例如:15 个字段)怎么办,我应该为每个字段创建一个失败 class 吗? 有没有更好的方法来处理验证?
My Question: What if I have a large form (eg:15 fields), Should I create a failure class for each of them? Is there a better way to handle the validation?
每个可能发生的错误都必须是可识别的。通过专用 classes、故障代码或常量。您选择哪一个取决于您要提供给客户端的错误上下文信息。
我也不会将消息放在失败对象中,因为由演示者来选择错误类型的演示。向用户显示错误的方式在很大程度上取决于用户界面。也许错误不需要字符串,它只是以图标或彩色标记等形式呈现。交互器不应创建特定于语言的字符串。这是用户界面的一部分。
在简单的情况下,您可能只需要一个错误代码或常量,如 401
,演示者将其转换为 Login failed
。当然,对于这种情况,您也可以使用通用错误对象。
class失败{ 内部代码; }
在其他情况下,您可能希望显示更详细的错误消息,例如 E-mails under the domain '@somedomain.com' are not allowed to login.
。在这种情况下,您应该使用错误对象而不是仅使用简单的代码来提供详细信息。例如
class 登录域失败 { 字符串本地部分; 字符串域名 }
演示者然后可以使用此信息生成失败字符串或以其他方式显示错误,例如在输入字段中突出显示域名或您能想到的任何内容。