我需要应用一个自定义验证器到帐单地址字段。该视图显示多个地址字段,包括国家/地区下拉列表(带有美国和加拿大选项)和BillingPostalCode文本框。最初,我将正则表达式应用于允许美国或加拿大邮政编码的消息约定,如下所示:
[MessageBodyMember]
[Display(Name = "Billing Postal Code")]
[Required]
[StringLength(10, MinimumLength = 5)]
[RegularExpression("(^\\d{5}(-\\d{4})?$)|(^[ABCEGHJKLMNPRSTVXY]{1}\\d{1}[A-Z]{1} *\\d{1}[A-Z]{1}\\d{1}$)", ErrorMessage = "Zip code is invalid.")] // US or Canada
public string BillingPostalCode
{
get { return _billingPostalCode; }
set { _billingPostalCode = value; }
}以上将允许美国或加拿大的邮政编码。但是,企业所有者希望只有在BillingCountry下拉列表中分别选择了美国或加拿大时,表单才允许使用美国或加拿大邮政编码。在他的测试用例中,他选择了加拿大并输入了美国的邮政编码。这种情况应该是不允许的。
我最初尝试这样做的目的是把它放到视图中,尽管我对创建2个textbox字段不满意。我应该只需要一个字段。
<div style="float: left; width: 35%;">
Zip Code<br />
<span id="spanBillingZipUS">
@Html.TextBoxFor(m => m.BillingPostalCode, new { @class = "customer_input", @id = "BillingPostalCode" })
</span>
<span id="spanBillingZipCanada">
@Html.TextBoxFor(m => m.BillingPostalCode, new { @class = "customer_input", @id = "BillingPostalCodeCanada" })
</span>
@Html.ValidationMessageFor(m => m.BillingPostalCode)
<br />
</div>我的想法是,当切换Country下拉列表时,我将使用jQuery显示或隐藏适当的跨度。这段很简单。
但我遇到的问题是,两个文本框都应用了一个验证器,它映射到上面粘贴的MessageBodyMember。我知道如何用jQuery编写验证代码,但我更希望将验证也应用于服务器端。
我是MVC的新手,来自web表单。“老式”的web表单自定义验证很容易实现。我在网上找到的在MVC中进行自定义验证的示例非常复杂。起初,这似乎是一个非常基本的要求。代码需要计算一个变量(选定的国家),并将该国家的适当正则表达式应用于BillingPostalCode字段。
如何使用MVC3以直接的方式满足这一要求?谢谢。
发布于 2012-09-06 04:17:52
嗯,我实现了,它就像一个带有数据注释的chram。您确实需要做一些工作来更改对下拉值的检查,但这是我发现的使用数据注释和Unobtrusive实现验证的更优雅的方法。
下面是一个示例:
模型
...
[RequiredIf("IsUKResident", true, ErrorMessage = "You must specify the City if UK resident")]
public string City { get; set; }
...自定义属性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Mvc3ConditionalValidation.Validation
{
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private RequiredAttribute _innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// get a reference to the property this validation depends upon
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty(this.DependentProperty);
if (field != null)
{
// get the value of the dependent property
var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);
// compare the value against the target value
if ((dependentvalue == null && this.TargetValue == null) ||
(dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
{
// match => means we should try validating this field
if (!_innerAttribute.IsValid(value))
// validation failed - return an error
return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
string depProp = BuildDependentPropertyId(metadata, context as ViewContext);
// find the value on the control we depend on;
// if it's a bool, format it javascript style
// (the default is True or False!)
string targetValue = (this.TargetValue ?? "").ToString();
if (this.TargetValue.GetType() == typeof(bool))
targetValue = targetValue.ToLower();
rule.ValidationParameters.Add("dependentproperty", depProp);
rule.ValidationParameters.Add("targetvalue", targetValue);
yield return rule;
}
private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
// build the ID of the property
string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);
// unfortunately this will have the name of the current field appended to the beginning,
// because the TemplateInfo's context has had this fieldname appended to it. Instead, we
// want to get the context as though it was one level higher (i.e. outside the current property,
// which is the containing object (our Person), and hence the same level as the dependent property.
var thisField = metadata.PropertyName + "_";
if (depProp.StartsWith(thisField))
// strip it off again
depProp = depProp.Substring(thisField.Length);
return depProp;
}
}
}客户端
...
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script>
$.validator.addMethod('requiredif',
function (value, element, parameters) {
var id = '#' + parameters['dependentproperty'];
// get the target value (as a string,
// as that's what actual value will be)
var targetvalue = parameters['targetvalue'];
targetvalue =
(targetvalue == null ? '' : targetvalue).toString();
// get the actual value of the target control
// note - this probably needs to cater for more
// control types, e.g. radios
var control = $(id);
var controltype = control.attr('type');
var actualvalue =
controltype === 'checkbox' ?
control.attr('checked').toString() :
control.val();
// if the condition is true, reuse the existing
// required field validator functionality
if (targetvalue === actualvalue)
return $.validator.methods.required.call(
this, value, element, parameters);
return true;
}
);
$.validator.unobtrusive.adapters.add(
'requiredif',
['dependentproperty', 'targetvalue'],
function (options) {
options.rules['requiredif'] = {
dependentproperty: options.params['dependentproperty'],
targetvalue: options.params['targetvalue']
};
options.messages['requiredif'] = options.message;
});
</script>
...
<div class="editor-label">
@Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.City)
@Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.IsUKResident)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.IsUKResident)
@Html.ValidationMessageFor(model => model.IsUKResident)
</div>
...发布于 2012-09-06 04:19:53
这听起来不像是使用ModelBinding和validation属性就能轻松完成的事情。
然后,当它到达服务器时,执行一些手动验证。
例如。
[Post]
public ActionResult SaveInfo(MyViewModel viewModel)
{
var isValid = true;
if (ModelState.IsValid)
{
if (!IsValidPostCode(viewModel))
{
isValid = false;
ModelState.AddModelError("BillingPostalCode", "The billing postcode appears to be invalid.");
}
if (isValid)
{
return RedirectToAction("success");
}
}
return View(viewModel);
}
private static IDictionary<string, string> countryPostCodeRegex = new Dictionary<string, string>
{
{ "USA", "USAPostCodeRegex" },
{ "Canada", "CanadianPostCodeRegex" },
}
private bool IsValidPostCode(MyViewModel viewModel)
{
var regexString = countryPostCodeRegex[viewModel.SelectedCountry];
var regexMatch = Regex.Match(viewModel.BillingPostalCode, regexString, RegexOptions.IgnoreCase);
return regexMatch.Success;
}发布于 2012-09-06 04:31:24
我在我的一个项目中使用了MVC Foolproof Validation。对于隐藏和显示字段,您将按照您所说的那样做,但是验证将遵循相同的逻辑。有2个字段用于邮政编码。一个是加拿大,另一个是美国。它们各自都有自己的正则表达式验证,以确保正确的格式。
伪码
[RequiredIf(DependentName = "Country", DependentValue="USA")]
public string PostalCodeUSA { get; set; }
[RequiredIf(DependentName = "Country", DependentValue="Canada")]
public string PostalCodeCanada { get; set; }https://stackoverflow.com/questions/12288534
复制相似问题