Effective C# Item39 : 使用.NET验证
软件开发过程中,安全始终是一个非常重要的话题,如何能保证恶意数据数据不会进入到数据库中,如何使得软件“水火不侵”,经常会成为折磨开发人员的噩梦。保证程序安全的一个重要手段就是对软件的入力数据进行检查,入力数据可能来自于用户直接的手动入力,也可能来自于外部的其他模块,我们需要在业务逻辑执行之前,对数据进行检查,保证只有“正确”的数据才能执行业务逻辑。
有时执行这种检查是很繁琐的,.NET为我们提供了一些验证机制。针对桌面应用程序和Web应用程序,.NET为我们提供了不同的方式来对数据进行验证。
针对桌面应用程序,针对需要进行验证的控件,我们需要为其注册Validating事件,在该事件中,编写数据检查的逻辑,同时还需要将控件的CauseValidation属性设置为true,否则,是不会触发Validating事件的。
我们来看下面的代码。
1 /// <summary>
2 /// test windows form validation.
3 /// </summary>
4 private static void WinFormVailidateTest()
5 {
6 Form frmTest = new Form();
7 TextBox txtName = new TextBox();
8 txtName.Validating += new System.ComponentModel.CancelEventHandler(txtName_Validating);
9 frmTest.Controls.Add(txtName);
10 //test validate with CauseValidation is true.
11 txtName.CausesValidation = true;
12 Console.WriteLine("Input With Validation:");
13 txtName.Text = "aaa";
14 frmTest.ValidateChildren();
15 txtName.Text = "aaaaaaa";
16 frmTest.ValidateChildren();
17
18 Console.WriteLine();
19
20 //test validate with CauseValidation is false.
21 txtName.CausesValidation = false;
22 Console.WriteLine("Input Without Validation:");
23 txtName.Text = "aaa";
24 frmTest.ValidateChildren();
25 txtName.Text = "aaaaaaa";
26 frmTest.ValidateChildren();
27 }
28
29
30 /// <summary>
31 /// textbox's validation event handler.
32 /// </summary>
33 /// <param name="sender"></param>
34 /// <param name="e"></param>
35 private static void txtName_Validating(object sender, System.ComponentModel.CancelEventArgs e)
36 {
37 TextBox txtTemp = sender as TextBox;
38 if (txtTemp == null || string.IsNullOrEmpty(txtTemp.Text))
39 {
40 return;
41 }
42
43 if (txtTemp.Text.Length > 5)
44 {
45 Console.WriteLine("Your input is InValid !");
46 e.Cancel = true;
47 return;
48 }
49 Console.WriteLine("The Text you input is : " + txtTemp.Text);
50 }
上面的代码中,我们定义了一个Form和一个TextBox,然后为TextBox控件注册Validating事件,在该事件中,判断如果TextBox中的文本长度超过5,就认为是非法的。然后将TextBox的Text属性设置为不同的值,调用Form的ValidateChildren方法,来触发TextBox的Validating事件。
上述代码的执行结果如下所示。
我们可以看到,当将控件的CauseValidation属性设置为false后,是不会触发Validating事件的。
在上面代码中,我们调用了Form的ValidateChildren方法,该方法会遍历画面中的所有控件,然后调用控件的Validating事件(如果有),当某个控件是一个容器(例如Panel、GroupBox等)时,它会采用递归的方式查找容器中的子控件,下面是针对ValidateChildren方法的一段测试代码,我们将上述的TextBox控件放置到一个Panel中,发现这时TextBox的Validating事件还是可以被触发的。
1 /// <summary>
2 /// test windows form validation for control.
3 /// test Form.ValidateChildren method.
4 /// </summary>
5 private static void ValidateChildrenTest()
6 {
7 Form frmTest = new Form();
8 TextBox txtName = new TextBox();
9 txtName.Validating += new System.ComponentModel.CancelEventHandler(txtName_Validating);
10 frmTest.Controls.Add(txtName);
11 //test validate with textbox is in panel or groupbox that has its own child control.
12 frmTest.Controls.Remove(txtName);
13 Panel plTemp = new Panel();
14 plTemp.Controls.Add(txtName);
15 frmTest.Controls.Add(plTemp);
16 txtName.CausesValidation = true;
17 Console.WriteLine("Validate Control's Input:");
18 txtName.Text = "aaa";
19 frmTest.ValidateChildren();
20 txtName.Text = "aaaaaaa";
21 frmTest.ValidateChildren();
22 }
上述代码的执行结果如下所示。
我们还可以使用以下的方式遍历画面中的控件,对其进行验证。
1 private void ValidateAllChildren( Control parent )
2 {
3 // If validation already failed, stop checking.
4 if( this.DialogResult == DialogResult.None )
5 return;
6
7 // For every control
8 foreach( Control c in parent.Controls )
9 {
10 // Give it focus
11 c.Focus( );
12
13 // Try and validate:
14 if (!this.Validate( ))
15 {
16 // when invalid, don't let the dialog close:
17 this.DialogResult = DialogResult.None;
18 return;
19 }
20 // Validate children
21 ValidateAllChildren( c );
22 }
23 }
24
下面我们再来看.NET如何对Web应用程序进行验证,.NET提供了5种不同类型的Validator:RequiredFieldValidator、RangeValidator、CompareValidator、RegularExpressionValidator和CustomValidator,其中前三个是比较简单的,第四个需要使用正则表达式,依靠的是正则表达式的强大功能,最后一种CustomValidator是最复杂的一个,我们可以从CustomValidator类中派生一个类,来完成业务自定制的验证,但是一般情况下,我们尽量避免使用CustomValidator,因为CustomValidator在验证时,使用的是脚本,例如JavaScript,这样我们很可能需要使用C#和JavaScript两种语言来编写同样的验证逻辑,一个用于客户端,一个用于服务器端,这样做是比较繁琐的,我们应该尽量使用前四种类型的Validator。
我们来看前四种Validator如何使用,请看下面的代码。
1 /// <summary>
2 /// test aspx page validation.
3 /// </summary>
4 private static void AspxValidateTest()
5 {
6 Page pgTest = new Page();
7 System.Web.UI.WebControls.TextBox txtName = new System.Web.UI.WebControls.TextBox();
8 txtName.ID = "txtName";
9 pgTest.Controls.Add(txtName);
10 //RequiredFieldValidator
11 Console.WriteLine("RequiredValidator:");
12 System.Web.UI.WebControls.RequiredFieldValidator requiredValidator = new System.Web.UI.WebControls.RequiredFieldValidator();
13 requiredValidator.ID = "requiredValidator";
14 requiredValidator.ControlToValidate = txtName.ID;
15 requiredValidator.ErrorMessage = "Required input!";
16 pgTest.Controls.Add(requiredValidator);
17 txtName.Text = string.Empty;
18 Validate(requiredValidator);
19 txtName.Text = "aaa";
20 Validate(requiredValidator);
21
22 Console.WriteLine();
23
24 //RangedValidator
25 Console.WriteLine("RangeValidator");
26 System.Web.UI.WebControls.RangeValidator rangeValidator = new System.Web.UI.WebControls.RangeValidator();
27 rangeValidator.ID = "rangeValidator";
28 rangeValidator.ControlToValidate = txtName.ID;
29 rangeValidator.MinimumValue = "a";
30 rangeValidator.MaximumValue = "z";
31 rangeValidator.Type = System.Web.UI.WebControls.ValidationDataType.String;
32 rangeValidator.ErrorMessage = "Out of Range !";
33 pgTest.Controls.Add(rangeValidator);
34 txtName.Text = "!@#";
35 Validate(rangeValidator);
36 txtName.Text = "a";
37 Validate(rangeValidator);
38
39 Console.WriteLine();
40
41 //CompareValidator
42 Console.WriteLine("CompareValidator:");
43 System.Web.UI.WebControls.TextBox txtMin = new System.Web.UI.WebControls.TextBox();
44 txtMin.ID = "txtMin";
45 System.Web.UI.WebControls.TextBox txtMax = new System.Web.UI.WebControls.TextBox();
46 txtMax.ID = "txtMax";
47 pgTest.Controls.Add(txtMax);
48 pgTest.Controls.Add(txtMin);
49 System.Web.UI.WebControls.CompareValidator compareValidator = new System.Web.UI.WebControls.CompareValidator();
50 compareValidator.ID = "compareValidator";
51 compareValidator.ControlToValidate = txtMin.ID;
52 compareValidator.ControlToCompare = txtMax.ID;
53 compareValidator.Operator = System.Web.UI.WebControls.ValidationCompareOperator.LessThan;
54 compareValidator.ErrorMessage = "Compare Fail !";
55 pgTest.Controls.Add(compareValidator);
56 txtMin.Text = "b";
57 txtMax.Text = "a";
58 Validate(compareValidator);
59 txtMax.Text = "c";
60 Validate(compareValidator);
61 }
62
63 /// <summary>
64 /// validate with validator.
65 /// </summary>
66 /// <param name="validator"></param>
67 private static void Validate(System.Web.UI.WebControls.BaseValidator validator)
68 {
69 if (validator == null)
70 {
71 return;
72 }
73 validator.Validate();
74 if (!validator.IsValid)
75 {
76 Console.WriteLine(validator.ErrorMessage);
77 }
78 else
79 {
80 Console.WriteLine("Your Input is Valid!");
81 }
82 }
上述代码,针对四种类型的Validator,都列出了两种情况,第一种情况是验证不通过,第二种情况是验证通过。执行结果如下。
通过上面的介绍,我们应该对.NET中的验证机制有一个大致的了解了,当然,上述提供的代码只是出于说明示范作用,在真实的环境中,情况要更加复杂多变,我们还需要见招拆招,具体问题具体分析。