当前的项目:
- ASP.NET 4.5.2
- MVC 5
- 流利的验证
我在这里可能有一个非常奇怪的问题。我可能正在经历“验证泄漏”,即对一个子模型(应该完全填写)的验证“泄漏”到第二次和第三次导入的同一模型中,但不一定是填写。
所以我有这个注册表单,它在注册后出现。如果不填写表格,用户将无法进入该网站。本质上,一个对站点内部正常运行至关重要的个人资料页面。
一类用户,称为招聘人员,需要三个“地址”:对于公司,对于他们作为联系人(如果他们的办公室不同),以及对于计费(如果计费地点不同)。只有公司地址是必需的,因为它需要手动输入地址。如果第二个和第三个与第一个相同,用户可以通过Bootstrap Switch控制的布尔值来专门标记它们,第二个和第三个子模型可以提交为空。如果两个布尔值(“此地址与主地址相同”)中的任何一个设置为 True(这很重要!),则仅需要在第一个子模型上进行验证
我最初的解决方案(后来我放弃了)是让 Address 子模型准确反映 DB 模型,因为它不仅有物理地址,还有电话号码、电子邮件等。我放弃了因为我在使用整个地址模型时遇到了验证问题,因为即使我的验证器只应该在地址不同时触发(我使用引导开关来允许这些地址字段作为组隐藏,因此如果第二个和第三个地址确实不同,用户可以取消隐藏地址字段)。
从那以后,我重新设计了 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;
}
}
那里有很多东西,对不起数据转储。可能只有前半部分很重要。
我的地址验证:
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-Z\u00C0-\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-Z\u00C0-\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.");
});
}
}
现在我的招聘人员验证:
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 属性修饰,所以我唯一能想到的是.SetValidator()
MailingAddress 的验证“泄漏”到 Contact 和 Billing 模型中。
我已经通过在招聘人员的验证 (the .SetValidator()
) 中明确禁用联系人和计费的验证调用来确认这一点 - 当我这样做时,可以使用空子模型成功提交表单。问题是,如果其中任何一个的布尔值设置为 False,我需要能够验证它们以确保地址正确且完整。
我该如何克服呢?
疯狂的想法:
我刚刚意识到这个问题可能会从两端打击我:
- 因为导入的模型使用相同的字段名称,所以
mailingAddress
AddressModel 的验证器正在捕获其他模型中的其他字段名称。 - 因为不同的表单使用相同的验证器,所以它已经通过 加载
mailingAddress
,因此它已经加载并准备好验证一次contactAddress
并billingAddress
得到处理。因此,即使他们自己的验证器没有被显式加载,他们也会陷入验证中。
或者,也许我会同时受到两端的打击。我想在这里获得专家意见。
更新:
再想一想,我认为这里的问题不是“疯狂的想法”,因为当我明确删除.SetValidator()
完全从主要招聘人员验证中分配的子模型验证器时,验证问题就消失了。可以成功提交和处理空的联系人和帐单地址子模型。如果我上面的两个想法中的任何一个在起作用,这都不会发生。
问题是 - 如果这些不是空的,我确实需要对它们进行验证!那么为什么没有正确使用布尔值来确定是否为这些子表单分配验证呢?为什么包含子模型验证器,即使它应该仅由false
布尔值触发,导致子模型得到验证?