我想对于ASP.NET的Validator控件已经熟悉的不能再熟悉了。我们 已经习惯了用Validator控件来验证我们在表单的输入,并通过ValidationSummary来输出我们为Validator控件设置的Error message。不知道大家有没想过进一步改进一下我们的Validation来改善我们的User Experience。比如,在ValidationSummary输出一个Link连接到对应的控件,而不是显示单纯的Error message。
比如在上图中,是一个典型的Login的Page。我们有两个必填的字段:User name和Password。为此我定义两个RequiredFieldValidator。他们的Error message分别为:”User name is mandatory!”和”Password is mandatory!”。在未输入任何值得前提下Click “Sign in”按钮,Error Message被显示在ValidationSummary上面。不过和传统的Error message不同,显示在ValidationSummary上的实际上是两个链接,Click对应的Error message,光标会设置到对应的Textbox上。比如上图所示:单击”User name is mandatory!”,光标回到User name对应的Texbox。
一、首先来看看aspx
现在我们来简单叙述上面的效果是如果实现的,在开始之前我想说的是,方法非常简单—或许你已经猜到了。下面是上面创建的用于登录的Web页面的HTML。
1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>
2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3: <html xmlns="http://www.w3.org/1999/xhtml">
4: <head id="Head1" runat="server">
5: <title>Login</title>
6: <style type="text/css">
7: body{}{font-family:Verdana; font-size:10px}
8: table{}{300px}
9: table tr{}{height:30px}
10: table td.firstColumn{}{100px; text-align:right}
11: table td.secondColumn{}{ text-align:left}
12: table span.asterisk{}{color:red}
13: table .textbox{}{150px; border:solid 1px #999999}
14: table .button{}{background-color: #00cc66;border:solid 1px #999999}
15: ul li{}{margin-bottom:5px}
16: ul li a{}{color:red; text-decoration:none}
17: ul li a:hover{}{text-decoration:underline}
18: </style>
19:
20: <script type="text/javascript">1:
2: function setFocus(control) {3: var controlToValidate = document.getElementById(control);4: controlToValidate.focus();
5: }
6:
7:
</script>
21: </head>
22: <body style="font-family: Verdana">
23: <form id="form1" runat="server">
24: <div>
25: <table cellpadding="0" cellspacing="5px">
26: <tr>
27: <td colspan="2">
28: <asp:ValidationSummary runat="server" ID="vldLogin" />
29: </td>
30: </tr>
31: <tr>
32: <td class="firstColumn">
33: User Name: <span class="asterisk"> *</span></td>
34: <td class="secondColumn">
35: <asp:TextBox runat="server" ID="txtUserName" CssClass="textbox"></asp:TextBox>
36: <asp:RequiredFieldValidator runat="server" ID="rqfUserName" ControlToValidate="txtUserName" Display="None"></asp:RequiredFieldValidator>
37: <asp:CustomValidator runat="server" ID="ctmUserName" Display="None" OnServerValidate="ctmUserName_ServerValidate" ControlToValidate="txtUserName" ></asp:CustomValidator>
38: </td>
39: </tr>
40: <tr>
41: <td class="firstColumn">
42: Password: <span class="asterisk"> *</span></td>
43: <td class="secondColumn">
44: <asp:TextBox runat="server" ID="txtPassword" TextMode="Password" CssClass="textbox"></asp:TextBox>
45: <asp:RequiredFieldValidator runat="server" ID="rqfPassword" ControlToValidate="txtPassword" Display="None" ></asp:RequiredFieldValidator>
46: </td>
47: </tr>
48: <tr>
49: <td colspan="2" align="center">
50: <asp:Button runat="server" ID="btnSignIn" Text="Sign in" CssClass="button" />
51: <asp:Button runat="server" ID="ButtonCancel" Text="Cancel" CausesValidation="false"
52: CssClass="button" />
53: </td>
54: </tr>
55: </table>
56: </div>
57: </form>
58: </body>
59: </html>
在看到了上面的登录界面之后再看看上面的HTML,结构清晰得一目了然。所以我就不再进一步解释了。在这里我只需要提提定义在aspx的一段javascript函数:setFocus。通过它把focus设置到指定的控件。
1: <script type="text/javascript">
2: function setFocus(control)
3: {
4: var controlToValidate = document.getElementById(control);
5: controlToValidate.focus();
6: }
7: </script>
二、接着我们来看看后台代码
看完了HTML,我们来看看该登录Web页面的后台代码。下面的代码片断为你展示了该Web页面背后的所有代码,所有的机关就存在于Web页面的Load时间处理方法Page_Load方法中。
1: public partial class Login : System.Web.UI.Page
2: {
3: protected void Page_Load(object sender, EventArgs e)
4: {
5: if (this.IsPostBack)
6: {
7: return;
8: }
9:
10: this.rqfUserName.ErrorMessage = string.Format("{0} is mandatory!", "User name");
11: this.rqfPassword.ErrorMessage = string.Format("{0} is mandatory!", "Password");
12: this.ctmUserName.ErrorMessage = "Such a user has not registered!";
13:
14: this.MakeClickableErrorMessage();
15: }
16:
17: private void MakeClickableErrorMessage()
18: {
19: foreach (BaseValidator validator in this.Validators)
20: {
21: if (validator.ControlToValidate == string.Empty)
22: {
23: continue;
24: }
25: string clientID = this.FindControl(validator.ControlToValidate).ClientID;
26: string script = string.Format("<a href= \"javascript:setFocus('{0}');\">{1}</a>", clientID, validator.ErrorMessage);
27: validator.ErrorMessage = script;
28: }
29: }
30:
31: protected void ctmUserName_ServerValidate(object source, ServerValidateEventArgs args)
32: {
33: if (this.txtUserName.Text.Trim() != "adm")
34: {
35: args.IsValid = false;
36: return;
37: }
38:
39: args.IsValid = true;
40: }
41: }
上面代码也简单得一塌糊涂,除了MakeClickableErrorMessage这个方法,其他的都不值一提。显示在ValidationSummary中原本简单的literal error message就是通过上面的这个MakeClickableErrorMessage方法转变成超链接的。
1: private void MakeClickableErrorMessage()
2: {
3: foreach (BaseValidator validator in this.Validators)
4: {
5: if (validator.ControlToValidate == string.Empty)
6: {
7: continue;
8: }
9: string clientID = this.FindControl(validator.ControlToValidate).ClientID;
10: string script = string.Format("<a href= \"javascript:setFocus('{0}');\">{1}</a>", clientID, validator.ErrorMessage);
11: validator.ErrorMessage = script;
12: }
13: }
在上面的代码中,我遍历page中的每个验证控件。如果该验证具有对应ControlToValidate属性(对于一个验证控件来说,ControlToValidate并非一个必需的属性,如果没有指定该属性,其值为空字符串),直接进入下一个循环。然后我把原来只是弹出的文本转变成一个<a></a>块,然后再将其重新赋值给对应的Validator contorl的ErrorMessage property。比如对于rqfUserName RequiredFieldValidator来说,原来的Error message是”User name is mandatory!”,那么现在的Error message变成了:
1: <a href=”javascript: setFocus(‘txtUserName’);”> User name is mandatory!</a>
上面只是一个简单的小窍门,我们以此为例,来进一步介绍ASP.NET如何进行验证的的。为了简单起见,在这里我没法讨论所有的验证控件。只介绍RequiredFieldValidator和CustomValidator这两种验证控件的处理流程。
三、ASP.NET是如何实现客户端验证的?
我们通过IE来浏览上面的Page,我们可以通过查看源文件得到最终呈现出来的Web页面的HTML。对于上面提到的登录页面,在客户端呈现出来的HTML如下面的HTML所示。
1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2: <html xmlns="http://www.w3.org/1999/xhtml">
3: <head id="Head1"><title>
4: Login
5: </title>
6: <style type="text/css">
7: body{}{font-family:Verdana; font-size:10px}
8: table{}{300px}
9: table tr{}{height:30px}
10: table td.firstColumn{}{100px; text-align:right}
11: table td.secondColumn{}{ text-align:left}
12: table span.asterisk{}{color:red}
13: table .textbox{}{150px; border:solid 1px #999999}
14: table .button{}{background-color: #00cc66;border:solid 1px #999999}
15: ul li{}{margin-bottom:5px}
16: ul li a{}{color:red; text-decoration:none}
17: ul li a:hover{}{text-decoration:underline}
18: </style>
19: <script type="text/javascript">1:
2: function setFocus(control) {3: var controlToValidate = document.getElementById(control);4: controlToValidate.focus();
5: }
6:
</script>1:
2: </head>
3: <body style="font-family: Verdana">4: <form name="form1" method="post" action="Login.aspx" onsubmit="javascript:return WebForm_OnSubmit();" id="form1">5: <div>
6: <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />7: <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />8: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTg3OTM1NTM2MA9kFgICAw9kFgYCBQ8PFgIeDEVycm9yTWVzc2FnZQVKPGEgaHJlZj0gImphdmFzY3JpcHQ6c2V0Rm9jdXMoJ3R4dFVzZXJOYW1lJyk7Ij5Vc2VyIG5hbWUgaXMgbWFuZGF0b3J5ITwvYT5kZAIHDw8WAh8ABVI8YSBocmVmPSAiamF2YXNjcmlwdDpzZXRGb2N1cygndHh0VXNlck5hbWUnKTsiPlN1Y2ggYSB1c2VyIGhhcyBub3QgcmVnaXN0ZXJlZCE8L2E+ZGQCCw8PFgIfAAVJPGEgaHJlZj0gImphdmFzY3JpcHQ6c2V0Rm9jdXMoJ3R4dFBhc3N3b3JkJyk7Ij5QYXNzd29yZCBpcyBtYW5kYXRvcnkhPC9hPmRkZLFuksmAaQ+N5sw8K+rkFk3GqgSn" />9: </div>
10: <script type="text/javascript">11: <!--
12: var theForm = document.forms['form1'];13: if (!theForm) {14: theForm = document.form1;
15: }
16: function __doPostBack(eventTarget, eventArgument) {17: if (!theForm.onsubmit || (theForm.onsubmit() != false)) {18: theForm.__EVENTTARGET.value = eventTarget;
19: theForm.__EVENTARGUMENT.value = eventArgument;
20: theForm.submit();
21: }
22: }
23: // --></script>1:
2: <script src="/Artech.ClickableValidationSummary/WebResource.axd?d=07ZNXubMk-rxUjn0jMywXg2&t=632969324944906146" type="text/javascript">1: </script>
2: <script src="/Artech.ClickableValidationSummary/WebResource.axd?d=5q3WmDnqxzNvEfUc_QbMe5qdQO1LUQ4P7mwuv6CrIMk1&t=632969324944906146" type="text/javascript">1: </script>
2: <script type="text/javascript">3: <!--
4: function WebForm_OnSubmit() {5: if (typeof (ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false) return false;6: return true;7: }
8: // --></script>1:
2: <div>
3: <table cellpadding="0" cellspacing="5px">4: <tr>
5: <td colspan="2">6: <div id="vldLogin" style="color:Red;display:none;">7: </div>
8: </td>
9: </tr>
10: <tr>
11: <td class="firstColumn">12: User Name: <span class="asterisk"> *</span></td>13: <td class="secondColumn">14: <input name="txtUserName" type="text" id="txtUserName" class="textbox" />15: <span id="rqfUserName" style="color:Red;display:none;"></span>16: <span id="ctmUserName" style="color:Red;display:none;"></span>17: </td>
18: </tr>
19: <tr>
20: <td class="firstColumn">21: Password: <span class="asterisk"> *</span></td>22: <td class="secondColumn">23: <input name="txtPassword" type="password" id="txtPassword" class="textbox" />24: <span id="rqfPassword" style="color:Red;display:none;"></span>25: </td>
26: </tr>
27: <tr>
28: <td colspan="2" align="center">29: <input type="submit" name="btnSignIn" value="Sign in" onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("btnSignIn", "", true, "", "", false, false))" id="btnSignIn" class="button" /> 30: <input type="submit" name="ButtonCancel" value="Cancel" id="ButtonCancel" class="button" />31: </td>
32: </tr>
33: </table>
34: </div>
35: <script type="text/javascript">36: <!--
37: var Page_ValidationSummaries = new Array(document.getElementById("vldLogin"));38: var Page_Validators = new Array(document.getElementById("rqfUserName"), document.getElementById("ctmUserName"), document.getElementById("rqfPassword"));39: // --></script>1:
2:
3: <script type="text/javascript">4: <!--
5: var rqfUserName = document.all ? document.all["rqfUserName"] : document.getElementById("rqfUserName");6: rqfUserName.controltovalidate = "txtUserName";7: rqfUserName.errormessage = "<a href= \"javascript:setFocus(\'txtUserName\');\">User name is mandatory!</a>";8: rqfUserName.display = "None";9: rqfUserName.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";10: rqfUserName.initialvalue = "";11: var ctmUserName = document.all ? document.all["ctmUserName"] : document.getElementById("ctmUserName");12: ctmUserName.controltovalidate = "txtUserName";13: ctmUserName.errormessage = "<a href= \"javascript:setFocus(\'txtUserName\');\">Such a user has not registered!</a>";14: ctmUserName.display = "None";15: ctmUserName.evaluationfunction = "CustomValidatorEvaluateIsValid";16: var rqfPassword = document.all ? document.all["rqfPassword"] : document.getElementById("rqfPassword");17: rqfPassword.controltovalidate = "txtPassword";18: rqfPassword.errormessage = "<a href= \"javascript:setFocus(\'txtPassword\');\">Password is mandatory!</a>";19: rqfPassword.display = "None";20: rqfPassword.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";21: rqfPassword.initialvalue = "";22: // --></script>1:
2: <div>
3: <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWBQL7uOMiAqXVsrMJArWptJELAsP3i5QHAv23gdwNI0m2v8hOJGGPTPLYqDLAkZE0nKU=" />4: </div>
5: <script type="text/javascript">6: <!--
7: var Page_ValidationActive = false;8: if (typeof (ValidatorOnLoad) == "function") {9: ValidatorOnLoad();
10: }
11:
12: function ValidatorOnSubmit() {13: if (Page_ValidationActive) {14: return ValidatorCommonOnSubmit();15: }
16: else {17: return true;18: }
19: }
20: // --></script>
20: </form>
21: </body>
22: </html>
我们从中提取对验证有用的信息。首先我们会看到有两个JavaScript被引用:
1: <script src="/Artech.ClickableValidationSummary/WebResource.axd?d=07ZNXubMk-rxUjn0jMywXg2&t=632969324944906146" type="text/javascript"></script>
2: <script src="/Artech.ClickableValidationSummary/WebResource.axd?d=5q3WmDnqxzNvEfUc_QbMe5qdQO1LUQ4P7mwuv6CrIMk1&t=632969324944906146" type="text/javascript"></script>
这两个JavaScript由ASP.NET生成。尤其内容较多,在这里先不列出他们的内容,等下面真正要使用到其中定义的JavaScript 在列出来。我们现在姑且称它们为JavaScript1和JavaScript2。在下面一段JavaScript中,为3个验证控件定义了3个客户端的对象,对象的名称和控件名称同名。并设置相关的属性:controltovalidate,errormessage,display,evaluationfunction。其中evaluationfunction为进行Validation的function的名称。
1: <script type="text/javascript">
2: <!--
3: var rqfUserName = document.all ? document.all["rqfUserName"] : document.getElementById("rqfUserName");
4: rqfUserName.controltovalidate = "txtUserName";
5: rqfUserName.errormessage = "<a href= \"javascript:setFocus(\'txtUserName\');\">User name is mandatory!</a>";
6: rqfUserName.display = "None";
7: rqfUserName.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
8: rqfUserName.initialvalue = "";
9: var ctmUserName = document.all ? document.all["ctmUserName"] : document.getElementById("ctmUserName");
10: ctmUserName.controltovalidate = "txtUserName";
11: ctmUserName.errormessage = "<a href= \"javascript:setFocus(\'txtUserName\');\">Such a user has not registered!</a>";
12: ctmUserName.display = "None";
13: ctmUserName.evaluationfunction = "CustomValidatorEvaluateIsValid";
14: var rqfPassword = document.all ? document.all["rqfPassword"] : document.getElementById("rqfPassword");
15: rqfPassword.controltovalidate = "txtPassword";
16: rqfPassword.errormessage = "<a href= \"javascript:setFocus(\'txtPassword\');\">Password is mandatory!</a>";
17: rqfPassword.display = "None";
18: rqfPassword.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
19: rqfPassword.initialvalue = "";
20: // -->
21: </script>
我们还发现通过Javascript定义了两个Array对象:Page_ValidationSummaries和Page_Validators。这两个Array用于保存Page中的所有的ValidationSummary和Validator control。
1: <script type="text/javascript">
2: <!--
3: var Page_ValidationSummaries = new Array(document.getElementById("vldLogin"));
4: var Page_Validators = new Array(document.getElementById("rqfUserName"), document.getElementById("ctmUserName"), document.getElementById("rqfPassword"));
5: // -->
6: </script>
我们知道,所有的Validation操作都是在Click “Sign In” 按钮之后进行的。我们来看看,该按钮的onClick事件处理程序是如何定义的:
通过onclick事件,我们可以看到,一个命名为WebForm_DoPostBackWithOptions的javascript function被调用,该function接收一个称为WebForm_PostBackOptions类型的对象。该类型被定一个在JavaScript1中(还记得JavaScript1指的是什么吗? 上溯到第三段)。
1: function WebForm_PostBackOptions(eventTarget, eventArgument, validation, validationGroup, actionUrl, trackFocus, clientSubmit) {
2: this.eventTarget = eventTarget;
3: this.eventArgument = eventArgument;
4: this.validation = validation;
5: this.validationGroup = validationGroup;
6: this.actionUrl = actionUrl;
7: this.trackFocus = trackFocus;
8: this.clientSubmit = clientSubmit;
9: }
该对象具有这样的表述的是关于Postback context的一些信息,比如:
-
eventTarget:Event触发的control,当前为” btnSignIn”。
-
eventArgument:Event额外的参数, 当前为””。
-
validation:是否进行Validation,当前为true。
-
validationGroup:eventTarget 对应的Validation group,这是ASP.NET 2.0的新特性,当当前为””,因为我没有设置btnSignIn的ValidationGroup的property。
-
actionUrl:表单被提交的Url,就像asp中Form的action一样。ASP.NET 1.x不提供cross-page的提交,在2.0中提供了此功能,当前为””, 我没有进行cross-page的提交。
-
trackFocus:是否进行焦点追踪,当前为false。
-
clientSubmit:是否通过form submit导致Postback,当前为false。
我们再来看看WebForm_DoPostBackWithOptions,像WebForm_PostBackOptions一样,该function同样被定义在JavaScript1中。
1: function WebForm_DoPostBackWithOptions(options) {
2: var validationResult = true;
3: if (options.validation) {
4: if (typeof(Page_ClientValidate) == 'function') {
5: validationResult = Page_ClientValidate(options.validationGroup);
6: }
7: }
8: if (validationResult) {
9: if ((typeof(options.actionUrl) != "undefined") && (options.actionUrl != null) && (options.actionUrl.length > 0)) {
10: theForm.action = options.actionUrl;
11: }
12: if (options.trackFocus) {
13: var lastFocus = theForm.elements["__LASTFOCUS"];
14: if ((typeof(lastFocus) != "undefined") && (lastFocus != null)) {
15: if (typeof(document.activeElement) == "undefined") {
16: lastFocus.value = options.eventTarget;
17: }
18: else {
19: var active = document.activeElement;
20: if ((typeof(active) != "undefined") && (active != null)) {
21: if ((typeof(active.id) != "undefined") && (active.id != null) && (active.id.length > 0)) {
22: lastFocus.value = active.id;
23: }
24: else if (typeof(active.name) != "undefined") {
25: lastFocus.value = active.name;
26: }
27: }
28: }
29: }
30: }
31: }
32: if (options.clientSubmit) {
33: __doPostBack(options.eventTarget, options.eventArgument);
34: }
35: }
在开始的时候,调用Page_ClientValidate进行客户端验证。我们来着重分析上面的javascript,看看具体的流程。Page_ClientValidate被定义在Javascript2中。
1: function Page_ClientValidate(validationGroup) {
2: Page_InvalidControlToBeFocused = null;
3: if (typeof(Page_Validators) == "undefined") {
4: return true;
5: }
6: var i;
7: for (i = 0; i < Page_Validators.length; i++) {
8: ValidatorValidate(Page_Validators[i], validationGroup, null);
9: }
10: ValidatorUpdateIsValid();
11: ValidationSummaryOnSubmit(validationGroup);
12: Page_BlockSubmit = !Page_IsValid;
13: return Page_IsValid;
14: }
上面的代码中,首先通过Page_Validators判断Page是否存在验证控件。我们在预先定义了Page_Validators 数组(还记得我们之前介绍的两个Array——Page_ValidationSummaries和Page_Validators吗?)。虽有遍历所有的验证控件,并调用ValidatorValidate方法执行每个验证控件的客户端验证。我们进一步看看ValidatorValidate又是如何定义的(ValidatorValidate定义在Javascript2中)。
1: function ValidatorValidate(val, validationGroup, event) {
2: val.isvalid = true;
3: if ((typeof(val.enabled) == "undefined" || val.enabled != false) && IsValidationGroupMatch(val, validationGroup)) {
4: if (typeof(val.evaluationfunction) == "function") {
5: val.isvalid = val.evaluationfunction(val);
6: if (!val.isvalid && Page_InvalidControlToBeFocused == null &&
7: typeof(val.focusOnError) == "string" && val.focusOnError == "t") {
8: ValidatorSetFocus(val, event);
9: }
10: }
11: }
12: ValidatorUpdateDisplay(val);
13: }
首先通过IsValidationGroupMatch判断验证控件的ValidationGroup是否和触发Postaback的Control对应的ValidationGroup相互匹配。因为只有在匹配的前提下才进行相关验证控件的验证工作。然后调用验证控件的evaluationfunction function来进行验证。通过前面的分析,我们知道RequiredFieldValidator的evaluationfunction为RequiredFieldValidatorEvaluateIsValid,而CustomValidator的evaluationfunction为CustomValidatorEvaluateIsValid。我们来看看这两个function是如何定义的。他们都定义在Javascript2中。
-
RequiredFieldValidatorEvaluateIsValid:通过正则表达式验证是否具有有效输入
1: function RequiredFieldValidatorEvaluateIsValid(val) {
2: return (ValidatorTrim(ValidatorGetValue(val.controltovalidate)) != ValidatorTrim(val.initialvalue))
3: }
4: function ValidatorGetValue(id) {
5: var control;
6: control = document.getElementById(id);
7: if (typeof(control.value) == "string") {
8: return control.value;
9: }
10: return ValidatorGetValueRecursive(control);
11: }
12: function ValidatorGetValueRecursive(control)
13: {
14: if (typeof(control.value) == "string" && (control.type != "radio" || control.checked == true)) {
15: return control.value;
16: }
17: var i, val;
18: for (i = 0; i<control.childNodes.length; i++) {
19: val = ValidatorGetValueRecursive(control.childNodes[i]);
20: if (val != "") return val;
21: }
22: return "";
23: }
24: function ValidatorTrim(s) {
25: var m = s.match(/^\s*(\S+(\s+\S+)*)\s*$/);
26: return (m == null) ? "" : m[1];
27: }
-
CustomValidatorEvaluateIsValid:实际上就是调用我们为CustomValidator设置的ClientValidationFunction
1: function CustomValidatorEvaluateIsValid(val) {
2: var value = "";
3: if (typeof(val.controltovalidate) == "string") {
4: value = ValidatorGetValue(val.controltovalidate);
5: if ((ValidatorTrim(value).length == 0) &&
6: ((typeof(val.validateemptytext) != "string") || (val.validateemptytext != "true"))) {
7: return true;
8: }
9: }
10: var args = { Value:value, IsValid:true };
11: if (typeof(val.clientvalidationfunction) == "string") {
12: eval(val.clientvalidationfunction + "(val, args) ;");
13: }
14: return args.IsValid;
15: }
在ValidatorValidate中,当我们通过调用各个验证控件的evaluationfunction来进行客户端端的验证后,对于没有通过验证的验证控件,通过调用ValidatorSetFocus设置相应控件的焦点。在这里就不在深入探讨了。接着通过调用ValidatorUpdateDisplay来根据我们制定的Display和不同浏览器,来设置错误消息的显示方式。
1: function ValidatorUpdateDisplay(val) {
2: if (typeof(val.display) == "string") {
3: if (val.display == "None") {
4: return;
5: }
6: if (val.display == "Dynamic") {
7: val.style.display = val.isvalid ? "none" : "inline";
8: return;
9: }
10: }
11: if ((navigator.userAgent.indexOf("Mac") > -1) &&
12: (navigator.userAgent.indexOf("MSIE") > -1)) {
13: val.style.display = "inline";
14: }
15: val.style.visibility = val.isvalid ? "hidden" : "visible";
16: }
实际上到现在为止,所有的验证工作已经完成。接下来我们来看看验证失败后相应的错误消息是如何显示的。所以我们要看看ValidatorUpdateDisplay的定义了。
分析完ValidatorValidate,我们在回到Page_ClientValidate上面。现在我们接着分析一下的执行流程。通过调用ValidatorValidate执行完各个验证控件的验证后,接着调用的是ValidatorUpdateIsValid()和ValidationSummaryOnSubmit(validationGroup)。ValidatorUpdateIsValid通过遍历每个验证控件来查看他们是否通过验证,最终确定这个页面是否通过验证。ValidationSummaryOnSubmit通过拼接字符串的形式在ValidationSummary显示对应的错误消息。这正是我们可以将错误消息写成超链接的原因所在。
1: function ValidatorUpdateIsValid() {
2: Page_IsValid = AllValidatorsValid(Page_Validators);
3: }
4: function AllValidatorsValid(validators) {
5: if ((typeof(validators) != "undefined") && (validators != null)) {
6: var i;
7: for (i = 0; i < validators.length; i++) {
8: if (!validators[i].isvalid) {
9: return false;
10: }
11: }
12: }
13: return true;
14: }
15: function ValidationSummaryOnSubmit(validationGroup) {
16: if (typeof(Page_ValidationSummaries) == "undefined")
17: return;
18: var summary, sums, s;
19: for (sums = 0; sums < Page_ValidationSummaries.length; sums++) {
20: summary = Page_ValidationSummaries[sums];
21: summary.style.display = "none";
22: if (!Page_IsValid && IsValidationGroupMatch(summary, validationGroup)) {
23: var i;
24: if (summary.showsummary != "False") {
25: summary.style.display = "";
26: if (typeof(summary.displaymode) != "string") {
27: summary.displaymode = "BulletList";
28: }
29: switch (summary.displaymode) {
30: case "List":
31: headerSep = "<br>";
32: first = "";
33: pre = "";
34: post = "<br>";
35: end = "";
36: break;
37: case "BulletList":
38: default:
39: headerSep = "";
40: first = "<ul>";
41: pre = "<li>";
42: post = "</li>";
43: end = "</ul>";
44: break;
45: case "SingleParagraph":
46: headerSep = " ";
47: first = "";
48: pre = "";
49: post = " ";
50: end = "<br>";
51: break;
52: }
53: s = "";
54: if (typeof(summary.headertext) == "string") {
55: s += summary.headertext + headerSep;
56: }
57: s += first;
58: for (i=0; i<Page_Validators.length; i++) {
59: if (!Page_Validators[i].isvalid && typeof(Page_Validators[i].errormessage) == "string") {
60: s += pre + Page_Validators[i].errormessage + post;
61: }
62: }
63: s += end;
64: summary.innerHTML = s;
65: window.scrollTo(0,0);
66: }
67: if (summary.showmessagebox == "True") {
68: s = "";
69: if (typeof(summary.headertext) == "string") {
70: s += summary.headertext + "\r\n";
71: }
72: var lastValIndex = Page_Validators.length - 1;
73: for (i=0; i<=lastValIndex; i++) {
74: if (!Page_Validators[i].isvalid && typeof(Page_Validators[i].errormessage) == "string") {
75: switch (summary.displaymode) {
76: case "List":
77: s += Page_Validators[i].errormessage;
78: if (i < lastValIndex) {
79: s += "\r\n";
80: }
81: break;
82: case "BulletList":
83: default:
84: s += "- " + Page_Validators[i].errormessage;
85: if (i < lastValIndex) {
86: s += "\r\n";
87: }
88: break;
89: case "SingleParagraph":
90: s += Page_Validators[i].errormessage + " ";
91: break;
92: }
93: }
94: }
95: alert(s);
96: }
97: }
98: }
99: }
四、ASP.NET是如何实现服务端验证的?
前面我们花了很大的篇幅介绍了客户端验证,通过介绍我们知道了,客户端验证和错误消息的显示均由Javascript来完成。现在我们来简单看看服务端验证。客户端通过调用__doPostBack实现向服务端的Postback(具体的Postback可以参考我的文章:《浅谈ASP.NET的Postback》),并进行验证控件的服务端验证,错误消息直接通过Html显示出来。比如下面是一段CustomValidator的验证逻辑。
1: protected void ctmUserName_ServerValidate(object source, ServerValidateEventArgs args)
2: {
3: if (this.txtUserName.Text.Trim() != "adm")
4: {
5: args.IsValid = false;
6: return;
7: }
8: args.IsValid = true;
9: }
如果上面的验证没有通过,最终呈现在客户端浏览器上的将如下图所示,后面是显示验证错误消息的HTML。