ViewModel嵌套并使用IValidatableObject进行业务逻辑相关验证
本文关键字:业务 验证 嵌套 IValidatableObject ViewModel | 更新日期: 2023-09-27 18:12:34
目前我正在呈现一个支付表单,在这个支付表单上可以切换是否显示信用卡验证。显示CVV字段依赖于一些配置,这些配置依赖于应用程序中几层的一些业务逻辑(在我的例子中,一些支付网关没有/需要CVV)。
表单显示以下字段:
- 持卡人名称
- 卡号
- 卡类型(Visa、运通卡、万事达卡等下拉菜单)
- 截止日期
- CVV取决于在数据库中找到的配置设置,变化
模型(Validate方法的问题)
public class IndexViewModel
{
public IEnumerable<CardType> CardTypes { get; set; }
public bool IsCvvEnabled { get; set; }
public PayProcess { get; set; }
}
public class ProcessViewModel : IValidatableObject
{
public string CardHolder { get; set; }
public string CardNumber { get; set; }
public string CardType { get; set; }
public string ExpiryDate { get; set; }
public string Cvv { get; set; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
// validation of properties CardHolder, Number, Type, etc...
// how do I read this value?
if (IsCvvEnabled)
{
int tempCvv;
if (string.IsNullOrEmpty(Cvv))
{
yield return new ValidationResult(Index.CVVRequired,
new[] { "CVV" });
}
else if (!int.TryParse(Cvv, out tempCvv))
{
yield return new ValidationResult(Index.CVVInvalid,
new[] { "CVV" });
}
}
}
}
<<h3>视图/h3>视图如下所示:
<section id="aligned">
<h3>@ViewRes.Index.Header</h3>
@Html.ValidationMessageFor(m => m.PayProcess.CreditCardType)
@Html.LabelFor(x => x.PayProcess.CreditCardType,
"Card Type")
@Html.DropDownListFor(m => m.PayProcess.CreditCardType,
Model.CreditCardTypes)
<!-- the other fields ... -->
@if (Model.PayProcess.IsCvvEnabled)
{
@Html.ValidationMessageFor(m => m.PayProcess.Cvv)
@Html.LabelFor(x => x.PayProcess.Cvv,
"Card Verification")
@Html.TextBoxFor(m => m.PayProcess.Cvv)
}
</section>
控制器public ActionResult Index()
{
var indexViewModel = CreateIndexViewModel(new ProcessViewModel());
return View(model);
}
private PayIndexViewModel CreateIndexViewModel(ProcessViewModel processViewModel)
{
if (!ModelState.IsValid)
{
return View("Index", CreateIndexViewModel(processViewModel));
}
// handle success scenario
}
private IndexViewModel CreateIndexViewModel(ProcessViewModel processViewModel)
{
var isCvvEnabled = _someDependency.Gateway.SupportsCvv;
var cardTypes = _someDependency.Gateway.GetSupportedCardTypes
.Select(x => new SelectListItem {
Text = x.Name,
Value = x.ID });
return new IndexViewModel
{
CardTypes = cardTypes,
IsCvvEnabled = isCvvEnabled,
PayProcess = processViewModel
};
}
问题 ProcessViewModel
只包含用户提交的表单输入值,它不包含IsCvvEnabled
,因为用户没有提交该值。
当验证需要此上下文信息时,我如何正确执行此验证?
如果用户没有在表单中提交值,那么您将不得不在post操作中手动设置它,或者您可以创建一个自定义模型绑定器来在验证之前设置该属性。然而,对于这样微不足道的事情,我认为这是太多的工作。
就我个人而言,我更喜欢的方式来实现你想要的,是重构你的验证方法的方法,在一个ModelState
,然后你自己调用它在你的post action:
viewModel.IsCvvEnabled = _someDependency.Gateway.SupportsCvv;
viewModel.Validate(this.ModelState);
if(!ModelState.IsValid)
{
return View(viewModel);
}
你必须把IsCvvEnabled属性放到ProcessViewModel中