当前项目:
- 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 “X1X-1X1”.")
.Matches(@"^([ABCEGHJKLMNPRSTVXY]d[ABCEGHJKLMNPRSTVWXYZ])-(d[ABCEGHJKLMNPRSTVWXYZ]d)$").WithMessage("Postal code must be Canada-valid, in the form of “X1X-1X1”");
});
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 “12345” or “12345-6789”.")
.Matches(@"^d{5}(?:[-s]d{4})?$").WithMessage("Zip code must be US-valid, in the form of “12345” or “12345-6789”.");
});
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 “123-456-7890”")
.Matches(@"^d{3}-d{3}-d{4}$").WithMessage("Phone number must be a valid 10-digit phone number with dashes, in the form of “123-456-7890”");
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 “123-456-7890”")
.Matches(@"^d{3}-d{3}-d{4}$").WithMessage("Phone number must be a valid 10-digit phone number with dashes, in the form of “123-456-7890”");
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,我需要能够验证它们,以确保地址是正确和完整的。
我该如何克服这一点?
疯狂思维:
我刚刚意识到这个问题可能会从两端袭来:
- 由于导入的模型使用相同的字段名称,
mailingAddress
AddressModel的验证器正在捕获其他模型中的其他字段名称 - 因为不同的表单使用相同的验证器,所以它已经通过
mailingAddress
加载,因此一旦contactAddress
和billingAddress
得到处理,它就已经加载并准备好进行验证。因此,即使他们自己的验证器没有被显式加载,他们也会陷入验证中
或者我可能同时受到两端的打击。我很想在这里发表专家意见。
更新:
转念一想,我不认为这是"疯狂的想法",因为当我明确地将子模型.SetValidator()
验证器分配完全从主要Recruiter验证中删除时,验证问题就消失了。空联系人和账单地址子模型可以成功提交和处理。如果我以上两个想法中的任何一个在起作用,这都不会发生。
问题是-如果这些不是空的,我确实需要验证它们!那么,为什么没有正确使用布尔值来确定是否将验证分配给这些子表单呢?为什么包含子模型验证器,即使假设仅由false
布尔值触发,也会导致子模型得到验证?
您可以尝试使用:
When(x => x.ContactSameAsAddress == false, () => {
RuleFor(x => x.contactAddress)
.SetValidator(new CreateRecruiterAddressValidator())
});