ASP.NET MVC 3 Validation - 正则表达式验证RegularExpressionAttribute之日期验证

时间:2022-04-07 07:46:57

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
{
public DateExpressionAttribute()
: base("Date Regular Expression Here")
{

}
}
较之方案(1)的做法,好处是能够把正则表达式封装起来,复用性更强(方案(1)中,每个日期验证都需要将正则表达式拷贝一遍,如此代码的维护工作量可想而知)。

建立一个测试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
{
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;
}
}
再次运行,方案(2)的客户端验证也可以成功触发了。


问题②的原因:
由于我们定义的两个字段都是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端验证通过后,再手动将字符串转化为日期格式。