2013-07-05 159 views
1

檢查輸入的最有效方法是什麼?驗證輸入的正確方法

  1. 如何防止嵌套和臃腫if語句?
  2. 正在使用異常做正確的方式嗎?如果不是,我應該遵循什麼樣的方法?

不好的例子(我認爲):

public int doSthWithAge(int age) 
{ 
    if (age > 0) 
    { 
     if (age > 100) 
     { 
      throw new AgeIsTooHighException(); 
     } 
    } 
    else 
    { 
     throw new NoWayException(); 
    } 
... 
} 

但什麼是好的方法?

(如果你給任何特定語言信息,請做到這一點,就好像我做的驗證在C#-syntax-wise-)

+1

您還沒有指定你的語言使用。在PHP中,有一組優秀的[過濾功能](http://php.net/manual/en/book.filter.php),可以爲你做所有這些。在您選擇的平臺中可能有類似的東西。 – 2013-07-05 15:17:49

+0

夠公平的,讓我編輯在C#中做這個問題# –

+0

這個問題問得太寬泛,因爲有很多有效的答案http://stackoverflow.com/help/dont-ask – Mgetz

回答

4

進行驗證的一個常見的面嚮對象的技術是您的驗證規則建模爲第一類對象:

  1. 定義的通用接口,用於驗證特定類型的數據的
  2. 實現的類或功能的集合符合該接口
  3. 循環此集合中的每個函數/對象並調用驗證方法。返回值可能是true/false,也可能是描述驗證失敗的對象(如果驗證規則已通過,則返回null)。建立的驗證失敗的列表,同時遍歷規則集合
  4. 目前驗證失敗的用戶以適當的方式

你會被許多圖書館在那裏看到它使用了這種技術。

實施例:本設計的

// the entity you want to validate 
public class Person 
{ 
    public int Age { get; set; } 
    public string Name { get; set; } 
} 

public class ValidationFailure 
{ 
    public ValidationFailure(string description) { Description = description; } 
    public string Description { get; set; } 
    // perhaps add other properties here if desired 
} 

// note that this is generic and can be reused for any object to validate 
public interface IValidationRule<TEntity> 
{ 
    ValidationFailure Test(TEntity entity); 
} 

public class ValidatesMaxAge : IValidationRule<Person> 
{ 
    public ValidationFailure Test(Person entity) 
    { 
     if (entity.Age > 100) return new ValidationFailure("Age is too high."); 
    } 
} 

public class ValidatesName : IValidationRule<Person> 
{ 
    public ValidationFailure Test(Person entity) 
    { 
     if (string.IsNullOrWhiteSpace(entity.Name)) 
      return new ValidationFailure("Name is required."); 
    } 
} 

// to perform your validation 
var rules = new List<IValidationRule> { new ValidatesMaxAge(), new ValidatesName() }; 
// test each validation rule and collect a list of failures 
var failures = rules.Select(rule => rule.Test(person)) 
    .Where(failure => failure != null); 
bool isValid = !failures.Any(); 

優點:

  • 順應的接口將促進一致性在代碼圖案
  • 一類或函數每驗證規則保持你的規則原子,可讀,自我記錄,可重複使用
  • 遵循驗證規則類的單一責任原則,並幫助簡化lify你的代碼需要執行驗證
  • 減少cyclomatic complexity(較少的嵌套if語句),因爲它是一個更加面向對象的或功能性的方法,而不是程序
  • 允許引進dependency injection個人驗證規則類,這是非常有用的如果你訪問數據庫或服務類進行驗證

編輯:@Mgetz提到輸入驗證VS業務驗證,這也是一個重要的考慮因素。上述的每類規則方法基於我每天工作的系統。我們更多地將它用於服務類中的業務邏輯驗證(這是一個包含大量業務規則的複雜的企業系統),並且設計符合我們的目的。我上面寫的具體規則非常簡單,看起來更像輸入驗證。對於輸入驗證,我建議使用更輕的方法,並在適當時將其與業務邏輯驗證分開。

這個設計的另一個實現是每個實體都有一個驗證器類,並且使用更輕量級的東西,例如lambda表達式來實現單個驗證規則。例如,流行的Fluent Validation庫使用這種方法。這是偉大的用戶輸入驗證,因爲它允許更少的代碼做簡單的驗證,並鼓勵你保持輸入驗證從業務邏輯驗證分開:

// Example using the FluentValidation library 
public class PersonValidator : AbstractValidator<Person> 
{ 
    public PersonValidator() 
    { 
     RuleFor(p => p.Age).LessThan(100); 
     RuleFor(p => p.Name).NotEmpty(); 
    } 
} 
+1

儘管如此,您應該小心,不要將業務邏輯移入驗證對象,這可能非常誘人。 – Mgetz

+0

@Mgetz,這是真的。你提到輸入驗證和業務驗證之間的區別很重要,而且我剛開始真正掌握和了解的內容。 –

+1

我應該修改我的聲明,可以使用驗證對象,但是應該只從其幫助驗證的實體中調用業務驗證對象。然而,具有自我驗證屬性通常更簡單和更容易(只有實體才能訪問setter,因此所有驗證都是在setter中執行的),但如果驗證失敗,則需要丟棄實例實例並重新加載實例。 – Mgetz

3

隨着驗證效率通常不關注。您應該關注兩種類型的驗證:

  1. 輸入驗證,例如,將用戶正在向服務器發送惡意旨在打破/闖入我的應用程序
  2. 業務驗證,它應該在你的業務邏輯發生了,應該差不多保持有效,一致的值。

跳過這兩者中的任何一個都是一種非常好的方法,最終可能導致被黑客或嚴重破壞的應用程序。

如果您使用ASP.net有圖書館(主要來自微軟)過多做前者,防XSS LIB等

後者將在你的共享業務邏輯得到最有效的預成型在實體本身。 E.G您的用戶實體不應允許年齡爲-1。

+0

我同意謹慎,但仍然想知道是否有更好的方法來進行驗證。此外,請專注於技術(如嵌套的ifs或Exception)而不是語言特定的庫,謝謝。 –