Effective C# 原則39:使用.Net驗證(譯)

來源:互聯網
上載者:User

Effective C# 原則39:使用.Net驗證
Item 39: Use .NET Validation

使用者的輸入可能是多種多樣的:你必須在互動控制項中儘可能的驗證輸入。寫一些使用者輸入驗證可能很做作,而且也有出錯的可能,但還是很有必要的。不能太相信使用者的輸入,使用者可能會輸入任何內容導致異常發生,進而進行SQL注入式攻擊。我們不希望任何類似這樣的事情發生。你應該瞭解足夠的資訊來懷疑使用者的輸入。很好,每個人都應該這樣做,這也就是為什麼.Net架構已經擴充了這樣的功能,你可以使用這些功能從而使自己的代碼編寫工作減到最小,因為我們要對使用者輸入的每一塊資料都要進行驗證。

.Net架構提供了不同的機制來驗證使用者的輸入,分別可以用在Web和Windows應用程式中。Web應用程式應該在瀏覽器上進行資料驗證,一般是使用JavaScript。一些驗證控制項在HTML面而中產生一些JS代碼,這對你的使用者來說是很有效:在對每一項輸入時,他們不用每次返回資料到服務上。這些Web控制項是使用Regex的擴充功能來完成對使用者輸入的驗證,這些驗證可以在頁面提交到伺服器之間完成。即使如此,你還是要在伺服器上做一些額外的驗證,以免受到程式式的攻擊。Windows就用程式使用不同的模式。使用者的輸入可以直接在應用程式中用C#代碼來驗證。所有的Windows控制項都是可驗證的,當你想通知使用者的非法輸入時。一般的模式是使用屬性訪問時的異常來指示非法的輸入。UI控制項捕獲這些異常然後顯示錯誤給使用者。

你可以使用5個web控制項來處理ASP.net應用程式中的大多數驗證任務。這5個控制項都是由屬性來控制這些要驗證的特殊的欄位。RequiredFieldValidator 強制使用者在給定欄位中輸入一個值,RangeValidator 要求特殊的欄位提供的值在給定範圍內,這個範圍可是一個數的大小,也可以是一個字串的長度。CompareValidator 可以讓你構造一個驗證規則來驗證表單上兩個同的控制項。這三個控制項都很簡單。最後兩個控制項提供了強大的功能,可以讓你根據你想要求的方法進行驗證。RegularExpression 驗證使用與此同時運算式來驗證使用者的輸入。如果與比較返回匹配,輸入的就是合法的。Regex是很有用的語言。你可以為你所有的實際情況建立Regex。VS.net包含了一些驗證的運算式,這可以協助你開始學習它。這有一些協助你學習更多Regex的有用資料,而且我強烈鼓勵你學習它。但我不能跑題而不給你提供一些最常用的構造。表5.1顯示了最常用的一些Regex元素,你可能會在你的應用程式中用來驗證輸入:

表5.1 常用的Regex

構造 含意
[a-z] 匹配單個小寫字元。括弧內的字元集中的任何字元與單個字元匹配。
\d 任何數字。
 
^,$ ^表示串的開始, $表示結束。
 
\w 匹配任何單詞.這是[A-Za-z0-9]簡寫。
 
(?NamedGroup\d{4,16}) 顯示兩個不同的常用元素,?NamedGroup 定義了一個特殊的變數來引用匹配。{4,16}匹配前面的構造至少4次最多16次。這一模式比對一個至少包含4個但不超過16個數位字串。如果匹配存在,那麼結果會儲存在NamedGroup中以便後面使用。

(a|b|c) 匹配a或b或c。 用堅線分開的是選擇操作:輸入的可是其中的任何一個。

(?(NamedGroup)a|b) 可選的。這與C#裡的三元操作等效,也就是說,如果NamedGroup 存在,匹配a,否則匹配b.

(譯註,關於Regex這裡只是簡單的說明了一下。覺得作者在這裡寫Regex很是不倫不類,即不全也不精。)

使用這些及Regex的構造,你可以發現你可以驗證使用者提交給你的任何內容。如果Regex還不夠,你還可以通過從CustomValidator 派生一個新在類添加你自己的驗證。這是一個不小的工作,而且我儘可能的避免它。當你用C#寫了一伺服器函數來驗證資料後,還要用ECMAscript寫一個用戶端的驗證函式。我討厭同樣的事做兩遍,而且我也儘可能的避免用ECMAscript寫任何內容,所以,我喜歡粘貼Regex式。

例如,這有一個Regex,用於驗證US的電話號碼。它接受區號用括弧括起來的,或者沒有括弧的,然後就是區號和號碼之間的空格,交換局號(exchange ),以及號碼。區號和交換局號之間的橫線也是可選的:

((\(\s*\d{3}\s*\))|(\d{3}))-?\s*\d{3}\s*-\s*\d{4}

通過查驗每一個組的運算式,這樣的邏輯是很清楚的:

((\(\s*\d{3}\s*\))|(\d{3}))-?

這和區號匹配,它充許(XXX)或者XXX的形式,其中XXX是三個數字。任何在數字周圍的空白字元是充許的。最後兩個字元,-和?,是許可但不要求一個橫線。

剩下的部份用於匹配電話的XXX-XXXX部份。\s匹配任意的空白,\d{3}匹配三個數字,\s*-\s*匹配一個圍繞在數字邊上的空白字元。最後,\d{4}精確匹配4個數字。

windows驗證工作方法小有不同,你沒有預先的驗證分析。相反,你要寫一個事件控制代碼到System.Windows.Forms.Control.Validating事件上,或者,如果你建立了你自己的控制項,重載OnValidating方法(參見原則35)。下面是一個標準的方法:

private void textBoxName_Validating( object sender,
  System.ComponentModel.CancelEventArgs e )
{
  string error = null;
  // Perform your test
  if ( textBoxName.Text.Length == 0 )
  {
    // If the test fails, set the error string
    // and cancel the validation event.
    error = "Please enter a name";
    e.Cancel = true;
  }
  // Update the state of an error provider with
  // the correct error text. Set to null for no
  // error.
  this.errorProviderAll.SetError( textBoxName, error );
}

你有幾個小工作要完成,以確保沒有不合法的輸入愉愉的混過去了。每一個控制項包含一個CausesValidation屬性,這個屬性決定這個控制項是否參與驗證。一般情況,你應該讓所有控制項的這一屬性為真,除非是Cancel按鈕。如果你忘記了,使用者還必須輸出正確的值以後才能取消對話方塊。第二個小任務是添加OK控制代碼來強制驗證所有的控制項。驗證只有在使用者訪問和離開控制項時觸發。如果使用者開啟了一個視窗,然後馬上點OK,你的所有驗證代碼都不會執行。為了修正這個,你要添加OK按鈕控制代碼,來訪問所有的控制項,然後強制驗證它們。下面兩個常規方法顯示了如何正確的完成任務。遞迴方法處理控制項以及它所包含的控制項:Tab頁面,控制群組以及控制項面板:

private void buttonOK_Click( object sender,
  System.EventArgs e )
{
  // Validate everyone:
  // Here, this.DialogResult will be set to
  // DialogResult.OK
  ValidateAllChildren( this );
}

private void ValidateAllChildren( Control parent )
{
  // If validation already failed, stop checking.
  if( this.DialogResult == DialogResult.None )
    return;

  // For every control
  foreach( Control c in parent.Controls )
  {
    // Give it focus
    c.Focus( );

    // Try and validate:
    if (!this.Validate( ))
    {
      // when invalid, don't let the dialog close:
      this.DialogResult = DialogResult.None;
      return;
    }
    // Validate children
    ValidateAllChildren( c );
  }
}

這些代碼可以處理大多數情況。一個特殊的快捷應用就是DataGrid/DataSet的組合。在設計時指定ErrorProvider的DataSource以及DataMember屬性:

ErrProvider.DataSource = myDataSet;
ErrProvider.DataMember = "Table1";

或者在運行時,調用BindToDataAndErrors 方法來同時設定:

ErrProvider.BindToDataAndErrors(  myDataSet, "Table1" );

錯誤會在設定DataRow.RowError 屬性以及調用DataRow.SetColumnError 方法時顯示特殊的錯誤。ErrorProvider 會在DataGrid的原始的行上的特殊儲存格裡顯示紅色的警告圖示。

大概的瞭解(whirlwind tour)了一下.net架構裡的控制項驗證,這可能對你很有協助,在很多應用程式中,你都可以建立出你所須要的高效驗證。使用者的輸入不能完全信任:使用者可能會出現錯誤,而且有時會有一些惡意的使用者試圖破壞你的應用程式。通過.Net架構已經提供的服務,你可以減少你自己的代碼編寫工作。驗證所有使用者的輸入,但要使用已經提供了的高效工具。
====================================

       

Item 39: Use .NET Validation
User input can come from a variety of locations: You must test input from data files as well as interactive controls. Writing user input validation is pedantic and error-prone but very necessary. Trusting user input can cause anything from exception conditions to SQL injection attacks. None of the options is pleasant. You know enough to be very skeptical of the validity of user input. Good. So does everyone else. That's why the .NET Framework has extensive capabilities that you can use to minimize the amount of code you need to write, yet still validate every piece of data that your users give you.

The .NET Framework provides different mechanisms to validate user input for web- and Windows-based applications. Web applications should get data validated at the browser, using JavaScript. The validation controls generate JavaScript in the HTML page. It's more efficient for your users: They do not need to have round-trips back to the server each time they change an entry. These web controls make extensive use of regular expressions to tentatively validate user input before the page is posted back to the server. Even so, you'll want to perform more extensive validation at the server, to prevent programmatic attacks. Windows applications use a different model. User input can be validated in C# code that runs in the same context as the application. The full gamut of Windows controls is available to you when you want to notify the user of invalid input. The general model uses exceptions in property accessors to indicate the invalid input. UI widgets catch those exceptions and display errors to the user.

You can use five web controls to handle most of the validation tasks in your ASP.NET applications. All five are controlled by properties that specify the field that should be validated and the conditions for valid input. RequiredFieldValidator forces the user to enter some value in a given field. RangeValidator mandates that a specific field supplies a value within a given range. This range could be the magnitude of a number or the length of a string value. CompareValidator lets you construct validation rules that relate two different fields in a web page. These three are relatively simple. The last two give you all the power you need to validate almost any user input you are expecting. The RegularExpression validator processes the user input using a regular expression. If the comparison returns a match, the user input is valid. Regular expressions are a very powerful language. You should be able to create a regular expression for any situation you have. Visual Studio .NET includes sample validation expressions that help get you started. There is a wealth of resources to help you learn all about regular expressions, and I strongly encourage you to do that. But I can't leave this topic without giving you a few of the most common constructs. Table 5.1 shows the most common regular expression elements you'll use for validating input in your applications.

Table 5.1. Common Regular Expression Constructs
Construct    Meaning
 
[a-z]    Matches any single lowercase letter. Anything inside square brackets matches a single character in the set.
 
\d    Any digit.
 
^,$    ^ is the beginning of the line, and $ is the end.
 
\w    Matches any "word" character. It is shorthand for [A-Za-z0-9].
 
(?NamedGroup\d{4,16})  Shows two different common elements. ?NamedGroup defines a variable that references the match. {4,16} matches the preceding construct at least 4 times but no more than 16. This pattern matches a string of at least 4 but no more than 16 digits. If a match is found, the match can be referred to later as NamedGroup.
 
(a|b|c)    Matches any of a, b, or c. Options separated by vertical bars are ORed: The input string can contain any one of them.
 
(?(NamedGroup)a|b)  Alternation. This is the equivalent of the ternary operator in C#. It means "If NamedGroup exists, match a, else match b."
 

Using these constructs and regular expressions, you will find that you can validate just about anything that your users throw at you. If regular expressions aren't enough for you, you can add your own validator by deriving a new class from CustomValidator. This is quite a bit of work, and I avoid it whenever I can. You write a server validator function using C#, and then you also write a client-side validator function using ECMAscript. I hate writing anything twice. I also avoid writing anything in ECMAscript, so I like to stick to regular expressions.

For example, here is a regular expression that validates U.S. phone numbers. It accepts area codes with or without parentheses around them, as well as any number of whitespace between the area code, exchange, and number. A dash between the area code and the exchange is also optional:

((\(\s*\d{3}\s*\))|(\d{3}))-?\s*\d{3}\s*-\s*\d{4}

 

By examining each group of expressions, the logic is clear:

((\(\s*\d{3}\s*\))|(\d{3}))-?

 

This matches the area code. It allows either (xxx) or xxx, where xxx is three digits. Any amount of whitespace surrounding the digits is acceptable. The last two characters, - and ?, allow but do not demand a dash.

The remaining portion matches the xxx-xxxx portion of the phone number. \s matches any amount of whitespace. \d{3} matches three digits. \s*-\s* matches a dash surrounded by any number of whitespace. Finally, \d{4} matches exactly four digits.

Windows validation works somewhat differently. No precooked validators parse input for you. Instead, you need to write an event handler for the System.Windows.Forms.Control.Validating event. Or, if you are creating your own custom control, override the OnValidating method (see Item 35). A standard form for a validation event handler follows:

private void textBoxName_Validating( object sender,
  System.ComponentModel.CancelEventArgs e )
{
  string error = null;
  // Perform your test
  if ( textBoxName.Text.Length == 0 )
  {
    // If the test fails, set the error string
    // and cancel the validation event.
    error = "Please enter a name";
    e.Cancel = true;
  }
  // Update the state of an error provider with
  // the correct error text. Set to null for no
  // error.
  this.errorProviderAll.SetError( textBoxName, error );
}

 

You have a few more small tasks to make sure that no invalid input sneaks through. Every control contains a CausesValidation property. This property determines whether the control participates in validation. In general, you should leave it true for all of your controls, except for the Cancel button. If you forget, the user must create valid input to cancel from your dialog box. The second small task is to add an OK handler to force validation of all controls. Validation happens only when a user visits and leaves a control. If the user opens a form and immediately presses OK, none of your validation code executes. To fix that, you add an OK button handler to walk through all your controls and force them to validate. The following two routines show you how to do this correctly. The recursive routines handle those controls that are also containers for other controls: tab pages, group boxes, and panels:

private void buttonOK_Click( object sender,
  System.EventArgs e )
{
  // Validate everyone:
  // Here, this.DialogResult will be set to
  // DialogResult.OK
  ValidateAllChildren( this );
}

private void ValidateAllChildren( Control parent )
{
  // If validation already failed, stop checking.
  if( this.DialogResult == DialogResult.None )
    return;

  // For every control
  foreach( Control c in parent.Controls )
  {
    // Give it focus
    c.Focus( );

    // Try and validate:
    if (!this.Validate( ))
    {
      // when invalid, don't let the dialog close:
      this.DialogResult = DialogResult.None;
      return;
    }
    // Validate children
    ValidateAllChildren( c );
  }
}

 

This code handles most normal cases. A special shortcut applies to the DataGrid/DataSet combination. Assign the ErrorProvider's DataSource and DataMember properties at design time:

ErrProvider.DataSource = myDataSet;
ErrProvider.DataMember = "Table1";

 

Or, at runtime, call the BindToDataAndErrors method to set both in one operation:

ErrProvider.BindToDataAndErrors(
  myDataSet, "Table1" );

 

Errors get displayed by setting the DataRow.RowError property and calling the DataRow.SetColumnError method to display specific errors. The ErrorProvider displays the red exclamation icon on the row and the specific cell in the DataGrid.

This whirlwind tour of the validation controls in the framework should help you efficiently create the validation you need in many applications. User input cannot be trusted: Users make mistakes, and occasionally malicious users try to break your application. By making the most use of the services already provided by the .NET Framework, you reduce the code you need to write. Validate all user input, but do it efficiently with the tools already provided.
 
   

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.