FluentValidation-验证"泄漏"到其他复制模型中



当前项目:

  • ASP。净额4.5.2
  • MVC 5
  • Fluent验证

我可能有一个非常奇怪的问题。我可能遇到了"验证泄漏",即对一个子模型(应该完全填写)的验证"泄漏"到第二次和第三次导入的同一模型中,但如果确实,则必须填写

所以我有这个注册表格,它是在注册后出现的。如果不填写表单,用户就无法进入网站。从本质上讲,配置文件页面对站点内部的正确操作至关重要。

一类被称为Recruiter的用户需要三个"地址":公司、联系人(如果他们的办公室不同)和计费(如果计费位置不同)。只有公司地址是必填的,因为它需要手动输入地址。如果第二个子模型和第三个子模型与第一个子模型相同,用户可以通过Bootstrap Switch控制的布尔值来具体标记它们,并且第二子模型和第三子模型可以作为空提交。如果两个布尔值("此地址与主地址相同")中的任何一个设置为True(这很重要!),则只需要在第一个子模型上进行验证。

我最初的解决方案(后来我放弃了)是让Address子模型准确地反映DB模型,因为它不仅有物理地址,还有电话号码、电子邮件等。我放弃了它,因为我在整个Address模型中遇到了验证问题,尽管我的验证器只有在地址不同时才会被触发,但这两个次级子模型仍在不断地进行验证(我使用了Bootstrap Switch,允许这些地址字段作为组隐藏,这样,如果第二个和第三个地址真的不同,用户就可以取消隐藏地址字段)。

此后,我重新设计了Address子模型,只包含物理地址:街道、城市、州等。不幸的是,我仍然遇到这个问题,因为第二和第三个子表单为空会对它们进行验证,即使用户将它们保留在默认配置中(此地址与主地址相同,不进行验证)。

我的地址模型:

public class CreateRecruiterAddressModel {
[DisplayName("Address")]
public string Address1 { get; set; }
[DisplayName("P.O. Box, Suite, etc.")]
public string Address2 { get; set; }
[DisplayName("City")]
public string City { get; set; }
#region For US and Canada
[DisplayName("Country")]
public Guid CountryId { get; set; }
#endregion
#region For US
[DisplayName("State")]
public Guid? StateId { get; set; }
[DisplayName("Zip Code")]
[DataType(DataType.PostalCode)]
public string ZipCode { get; set; }
#endregion
#region for Canada
[DisplayName("Province")]
public Guid? ProvinceId { get; set; }
[DisplayName("Postal Code")]
[DataType(DataType.PostalCode)]
public string PostalCode { get; set; }
#endregion
#region For everyone else
[DisplayName("Province")]
public string ProvinceName { get; set; }
[DisplayName("Country")]
public string CountryName { get; set; }
[DisplayName("Postal Code")]
[DataType(DataType.PostalCode)]
public string Postal { get; set; }
#endregion
}

正如你所看到的,我提供了一些决策部分:如果用户来自加拿大或美国,他们会得到一个省/州的自定义下拉列表,其他人会得到简单的国家和省文本字段。

我的招聘模式:

public class CreateRecruiterProfileViewModel {
[DisplayName("Company Name")]
public string CompanyName { get; set; }
public CreateRecruiterAddressModel mailingAddress { get; set; }
[DisplayName("Phone Number")]
[DataType(DataType.PhoneNumber)]
public string MailingPhone { get; set; }
[DisplayName("Fax Number")]
[DataType(DataType.PhoneNumber)]
public string MailingFax { get; set; }
[DisplayName("Contact Name")]
public string ContactName { get; set; }
[DisplayName("Is your contact address at this company the same as the company’s mailing address, above?")]
public bool ContactSameAsAddress { get; set; }
public CreateRecruiterAddressModel contactAddress { get; set; }
[DisplayName("Phone Number")]
[DataType(DataType.PhoneNumber)]
public string ContactPhone { get; set; }
[DisplayName("Extension")]
public short? ContactExtension { get; set; }
[DisplayName("eMail:")]
public string ContacteMail { get; set; }
[DisplayName("Name on the credit card")]
public string BillingName { get; set; }
[DisplayName("Is the billing address for this account the same as the company’s mailing address, above?")]
public bool BillingSameAsAddress { get; set; }
public CreateRecruiterAddressModel billingAddress { get; set; }
[DisplayName("Phone Number")]
[DataType(DataType.PhoneNumber)]
public string BillingPhone { get; set; }
[DisplayName("Extension")]
public short? BillingExtension { get; set; }
[DisplayName("eMail:")]
public string BillingeMail { get; set; }
[DisplayName("Website")]
public string Website { get; set; }
[DisplayName("Industry")]
public string IndustryId { get; set; }
[DisplayName("Number of Employees:")]
public string NumberEmployees { get; set; }
[DisplayName("Operating Since:")]
[DataType(DataType.Date)]
public DateTime? OperatingSince { get; set; }
[DisplayName("Operating Revenue:")]
public string OperatingRevenue { get; set; }

public Guid CountryNU {
get { return Settings.Default.CountryNU; }
}
public Guid CountryCA {
get { return Settings.Default.CountryCA; }
}
public Guid CountryUS {
get { return Settings.Default.CountryUS; }
}
private IEnumerable<SelectListItem> _CountryList;
public IEnumerable<SelectListItem> CountryList {
get { return SelectLists.CountryList(); }
set { _CountryList = value; }
}
private IEnumerable<SelectListItem> _StateList;
public IEnumerable<SelectListItem> StateList {
get { return SelectLists.ProvinceList(Settings.Default.CountryUS); } // Add US Guid 
set { _StateList = value; }
}
private IEnumerable<SelectListItem> _ProvinceList;
public IEnumerable<SelectListItem> ProvinceList {
get { return SelectLists.ProvinceList(Settings.Default.CountryCA); } // Add CA Guid 
set { _ProvinceList = value; }
}
private IEnumerable<SelectListItem> _IndustryList;
public IEnumerable<SelectListItem> IndustryList {
get { return SelectLists.IndustryList(); }
set { _IndustryList = value; }
}
public CreateRecruiterProfileViewModel() {
ContactSameAsAddress = true;
BillingSameAsAddress = true;
}
}

Lotsa的东西在那里,很抱歉数据转储。也许只有上半场才是重要的。

我的地址验证:

public class CreateRecruiterAddressValidator : AbstractValidator<CreateRecruiterAddressModel> {
public CreateRecruiterAddressValidator() {
RuleFor(x => x.Address1)
.NotEmpty().WithMessage("Please provide the current address.")
.Length(6, 128).WithMessage("Addresses should be between 6 and 128 characters long.");
RuleFor(x => x.City)
.NotEmpty().WithMessage("Please provide the current city.")
.Length(2, 64).WithMessage("City names should be between 2 and 64 characters long.");
RuleFor(x => x.CountryId)
.NotEmpty().WithMessage("Please choose the current country.");
When(x => x.CountryId == Settings.Default.CountryCA,
() => {
RuleFor(x => x.ProvinceId)
.NotEmpty().WithMessage("Please choose the current province.");
RuleFor(x => x.PostalCode)
.NotEmpty().WithMessage("Please enter a valid postal code.")
.Length(7, 7).WithMessage("Postal code must be in the form of &#8220;X1X-1X1&#8221;.")
.Matches(@"^([ABCEGHJKLMNPRSTVXY]d[ABCEGHJKLMNPRSTVWXYZ])-(d[ABCEGHJKLMNPRSTVWXYZ]d)$").WithMessage("Postal code must be Canada-valid, in the form of &#8220;X1X-1X1&#8221;");
});
When(x => x.CountryId == Settings.Default.CountryUS,
() => {
RuleFor(x => x.StateId)
.NotEmpty().WithMessage("Please choose the current state.");
RuleFor(x => x.ZipCode)
.NotEmpty().WithMessage("Please enter a valid zip code.")
.Length(5, 10).WithMessage("Zip code must be in the form of &#8220;12345&#8221 or &#8220;12345-6789&#8221;.")
.Matches(@"^d{5}(?:[-s]d{4})?$").WithMessage("Zip code must be US-valid, in the form of &#8220;12345&#8221 or &#8220;12345-6789&#8221;.");
});
When(x => x.CountryId == Settings.Default.CountryNU,
() => {
RuleFor(x => x.CountryName)
.NotEmpty().WithMessage("Please provide a valid country name.")
.Matches(@"[a-zA-Zu00C0-u01FF- ]+").WithMessage("Please enter only letters and spaces, no numbers or special characters.")
.Length(2, 64).WithMessage("Country names should be between 2 and 64 characters long.");
RuleFor(x => x.ProvinceName)
.NotEmpty().WithMessage("Please provide a valid province name.")
.Matches(@"[a-zA-Zu00C0-u01FF- ]+").WithMessage("Please enter only letters and spaces, no numbers or special characters.")
.Length(2, 64).WithMessage("Province names should be between 2 and 64 characters long.");
RuleFor(x => x.Postal)
.NotEmpty().WithMessage("Please provide a valid postal code.")
.Length(3, 10).WithMessage("Postal code must be between 3 and 10 digits long, and valid for the country of residence.");
});
}
}

现在是我的Recruiter验证:

public class CreateRecruiterProfileValidator : AbstractValidator<CreateRecruiterProfileViewModel> {
public CreateRecruiterProfileValidator() {
RuleFor(x => x.CompanyName)
.NotEmpty().WithMessage("Please provide a valid company name.")
.Length(2, 64).WithMessage("Company names should be between 2 and 64 characters long.");
RuleFor(x => x.mailingAddress)
.SetValidator(new CreateRecruiterAddressValidator()); // This is the validator I think screws up the bottom two
RuleFor(x => x.ContactName)
.NotEmpty().WithMessage("Please provide a valid contact name.")
.Length(2, 64).WithMessage("Contact names should be between 2 and 64 characters long.");
RuleFor(x => x.contactAddress)
.SetValidator(new CreateRecruiterAddressValidator())
.When(x => x.ContactSameAsAddress == false); // Problem one
RuleFor(x => x.ContactPhone)
.NotEmpty().WithMessage("Please enter a valid 10-digit phone number.")
.Length(12, 12).WithMessage("Phone number must be in the form of &#8220;123-456-7890&#8221;")
.Matches(@"^d{3}-d{3}-d{4}$").WithMessage("Phone number must be a valid 10-digit phone number with dashes, in the form of &#8220;123-456-7890&#8221;");
RuleFor(x => x.ContacteMail)
.NotEmpty().WithMessage("Please enter a valid eMail Address..")
.EmailAddress().WithMessage("Please provide a valid eMail address.");
RuleFor(x => x.BillingName)
.NotEmpty().WithMessage("Please provide a valid billing name.")
.Length(2, 64).WithMessage("Billing names should be between 2 and 64 characters long.");
RuleFor(x => x.billingAddress)
.SetValidator(new CreateRecruiterAddressValidator())
.When(x => x.BillingSameAsAddress == false); // Problem two
RuleFor(x => x.BillingPhone)
.NotEmpty().WithMessage("Please enter a valid 10-digit phone number.")
.Length(12, 12).WithMessage("Phone number must be in the form of &#8220;123-456-7890&#8221;")
.Matches(@"^d{3}-d{3}-d{4}$").WithMessage("Phone number must be a valid 10-digit phone number with dashes, in the form of &#8220;123-456-7890&#8221;");
RuleFor(x => x.BillingeMail)
.NotEmpty().WithMessage("Please enter a valid eMail Address..")
.EmailAddress().WithMessage("Please provide a valid eMail address.");
When(x => !string.IsNullOrEmpty(x.Website),
() => {
RuleFor(x => x.Website)
.Length(12, 64).WithMessage("A URL should be in the form of “http://www.domain.com/”")
.Matches(@"^http[s]?://").WithMessage("A URL should begin with “http://” or “https://”");
});
RuleFor(x => x.IndustryId)
.NotEmpty().WithMessage("Please choose the closest appropriate industry.");
}
}

根据我对验证的看法,联系人和账单地址模型的.SetValidator()只应在布尔标志设置为false时触发,但无论布尔标志是什么,它们都会出现,每次触发。与中一样,服务器返回表单(服务器端触发),并根据需要标记联系人和账单地址。这不是我想要的

Address模型没有被任何Validation属性修饰,所以我唯一能想到的是MailingAddress的.SetValidator()验证正在"泄漏"到Contact和Billing模型中。

我已经通过在招聘人员的验证(.SetValidator())中明确禁用联系人和账单的验证调用来确认这一点,并且当我这样做时,可以使用空的子模型成功提交表单。问题是,如果其中任何一个的布尔值设置为False,我需要能够验证它们,以确保地址是正确和完整的。

我该如何克服这一点?


疯狂思维:

我刚刚意识到这个问题可能会从两端袭来:

  • 由于导入的模型使用相同的字段名称,mailingAddressAddressModel的验证器正在捕获其他模型中的其他字段名称
  • 因为不同的表单使用相同的验证器,所以它已经通过mailingAddress加载,因此一旦contactAddressbillingAddress得到处理,它就已经加载并准备好进行验证。因此,即使他们自己的验证器没有被显式加载,他们也会陷入验证中

或者我可能同时受到两端的打击。我很想在这里发表专家意见。


更新:

转念一想,我不认为这是"疯狂的想法",因为当我明确地将子模型.SetValidator()验证器分配完全从主要Recruiter验证中删除时,验证问题就消失了。空联系人和账单地址子模型可以成功提交和处理。如果我以上两个想法中的任何一个在起作用,这都不会发生。

问题是-如果这些不是空的,我确实需要验证它们!那么,为什么没有正确使用布尔值来确定是否将验证分配给这些子表单呢?为什么包含子模型验证器,即使假设仅由false布尔值触发,也会导致子模型得到验证?

您可以尝试使用:

When(x => x.ContactSameAsAddress == false, () => { 
RuleFor(x => x.contactAddress)
.SetValidator(new CreateRecruiterAddressValidator())
});

相关内容

  • 没有找到相关文章

最新更新