ASP.NET MVC 3中,正则表达式的验证属性是RegularExpressionAttribute。
问题:
日期格式进行Server端和Client端的验证
解决方案(1):
直接使用RegularExpressionAttribute,写正则表达式。
[RegularExpression(@"Regular Expression Here", ErrorMessage = "Please input valid date format")]
public String RegualarExpressionField { get; set; }
这样做的好处是比较直接,但当正则表达式比较复杂时,代码会显得并不怎么优雅。
解决方案(2)
从RegularExpressionAttribute直接派生出DateExpressionAttribute
public class DateExpressionAttribute : RegularExpressionAttribute较之方案(1)的做法,好处是能够把正则表达式封装起来,复用性更强(方案(1)中,每个日期验证都需要将正则表达式拷贝一遍,如此代码的维护工作量可想而知)。
{
public DateExpressionAttribute()
: base("Date Regular Expression Here")
{
}
}
建立一个测试model:
public class TestModel
{
[DateExpression]
public DateTime? DateField { get; set; }
[RegularExpression(@"Regular Expression Here", ErrorMessage = "Please input valid date format")]
public String RegualarExpressionField { get; set; }
}
page:
<div>
<fieldset>
<legend>Account Information</legend>
<div class="editor-label">
@Html.LabelFor(m => m.DateField)
</div>
<div class="editor-field">
@Html.TextBoxFor(m => m.DateField)
@Html.ValidationMessageFor(m => m.DateField)
</div>
<div class="editor-label">
@Html.LabelFor(m => m.RegualarExpressionField)
</div>
<div class="editor-field">
@Html.TextBoxFor(m => m.RegualarExpressionField)
@Html.ValidationMessageFor(m => m.RegualarExpressionField)
</div>
<p>
<input type="submit" value="go!" />
</p>
</fieldset>
</div>
运行之后会发现两个问题:
①方案(2)中,客户端的验证没有被触发
②即使输入完全符合正则表达式的值,两个方案经Server端验证后,全部返回验证的错误信息
讨论:
问题①的原因:
我们在DateExpressionAttribute并没有实现IClientValidatable接口,故Client端的验证不会被触发;
修改类如下:
public class DateExpressionAttribute : RegularExpressionAttribute, IClientValidatable再次运行,方案(2)的客户端验证也可以成功触发了。
{
public DateExpressionAttribute()
: base(@"Your date regular expression here")
{
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentCulture,
"The filed {0} should be a date", name);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var name = metadata.GetDisplayName();
var rule = new ModelClientValidationRegexRule(FormatErrorMessage(name), Pattern);
yield return rule;
}
}
问题②的原因:
由于我们定义的两个字段都是DateTime类型,故从前台传到后台后,实际去进行验证的时间格式是DateTime(yyyy-MM-dd hh:mm:ss),所以正则表达式必然返回错误(几乎永远不可能通过)。另外,将Client端的验证关掉,输入abc后,返回的错误是"The value "abc" is not a valid value for DateField"。这个错误并不是正则表达式的验证错误,而是当试着把"abc"转换成DateTime类型的时候所抛出的错误,也就是说,你如果输入了无法转换成DateTime类型的值的话,验证是不会经过正则表达式那一端的。当你到达正则表达式验证的时候,输入的日期格式已经被转换成了DateTime了。
此时,可以考虑把DateExpression的Server端验证拿掉,最终代码如下:
public class DateExpressionAttribute : RegularExpressionAttribute, IClientValidatable
{
public DateExpressionAttribute()
: base(@"((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))")
{
}
public override bool IsValid(object value)
{
return true;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentCulture,
"The filed {0} should be a date", name);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var name = metadata.GetDisplayName();
var rule = new ModelClientValidationRegexRule(FormatErrorMessage(name), Pattern);
yield return rule;
}
}
若是仍需要加Server端的验证的话,另一个间接方法就是暂时用String来取得Date的值。这样一来不必经过DateTime类型的转化。即便输入了错误的格式,也能够进行Server端的验证,返回期望的错误消息。在Server端验证通过后,再手动将字符串转化为日期格式。