Una regola di validazione personalizzata con un custom attribute in ASP.NET

di Marco De Sanctis, in ASP.NET 3.5, ASP.NET 4.0, ASP.NET MVC,

Le DataAnnotation sono un sistema standard, nel .NET Framework, per esprimere regole validazione tramite una sintassi basata su attributi. I diversi modelli di sviluppo che oggi abbiamo a disposizione, ad esempio ASP.NET Dynamic Data Controls o ASP.NET MVC (dalla versione 2), sono in grado poi di interpretare questo codice dichiarativo, applicando le varie regole per determinare se l'input dell'utente sia valido o meno.

In ASP.NET MVC, ad esempio, è sufficiente decorare il model User come segue

public class User
{
  [Required]
  public string Username { get; set; }
        
  [Required]
  [DataType(DataType.Password)]
  public string Password { get; set; }
        
  [Required]
  [DataType(DataType.Password)]
  [Compare("Password")]
  public string ConfirmPassword { get; set; }
}

e successivamente interrogare la proprietà ModelState.IsValid, per far sì che il framework verifichi che:

  • tutte le proprietà siano popolate, visto che tutte sono marcate come Required;
  • il contenuto dell proprietà ConfirmPassword corrisponda a quello di Password, come specificato dall'attributo Compare.

La più grande potenzialità delle DataAnnotation è però costituita dalla possibilità di realizzare attributi personalizzati per implementare le proprie regole di validazione. Supponiamo ad esempio che, per ragioni di sicurezza, vogliamo fare in modo che la password contenga almeno uno tra alcuni simboli specificati. Per questi scopi può sicuramente tornare utile l'attributo RegularExpressionAttribute, che effettua una validazione in base ad una regular expression fornita come parametro. Alternativamente, però, volendo avvalersi di una sintassi più semplice, possiamo pensare di realizzarne una nostra versione, così che possiamo sfruttarla in maniera simile alla seguente:

[Required]
[MustContain("!£$%")]
[DataType(DataType.Password)]
public string Password { get; set; }

Per raggiungere questo scopo è sufficiente creare una classe che erediti da ValidationAttribute, come nell'esempio:

public class MustContainAttribute : ValidationAttribute
{
  public string Chars { get; set; }

  public MustContainAttribute(string chars)
    // Messaggio di default 
    : base("Il campo {0} deve contenere almeno un carattere tra {1}")
  {
     this.Chars = chars;
  }

}

Essa definisce una proprietà Chars, inizializzata tramite il costruttore, che utilizzeremo per memorizzare l'elenco dei caratteri richiesti. La regola di validazione vera e propria, invece, può essere implementata effettuando l'override del metodo IsValid:

protected override ValidationResult IsValid(
  object value, ValidationContext validationContext)
{
  var stringValue = value as string;

  if (stringValue.Any(c => Chars.Contains(c)))
    return null;

  return new ValidationResult(
    this.FormatErrorMessage(validationContext.DisplayName));
}

Questo metodo riceve in ingresso un parametro value, che contiene il dato che dobbiamo validare, più un ValidationContext tramite cui possiamo reperire, se necessarie, ulteriori informazioni relative all'operazione di validazione in atto, come l'istanza completa dell'oggetto o il nome della proprietà validata. La logica del metodo è molto semplice, e si limita a verificare, tramite l'extension method Any di LINQ, la presenza all'interno della stringa in input di almeno uno dei caratteri di Chars.

Nel caso la validazione fallisca, viene restituita un'istanza di ValidationResult contenente il messaggio d'errore. Quest'ultimo può essere eventalmente personalizzato effettuando l'override del metodo FormatErrorMessage, includendo ad esempio l'elenco dei caratteri richiesti oltre al nome della proprietà errata.

public override string FormatErrorMessage(string name)
{
  return string.Format(this.ErrorMessageString, name, Chars);
}

L'implementazione in alto, in particolare, genera il messaggio di errore utilizzando come template la proprietà ErrorMessageString. Essa, infatti, viene automaticamente popolata dalla classe base in base al valore di ErrorMessage, ErrorMessageResourceName ed ErrorMessageResourceType, se definiti dall'utente.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi