2009-10-13 4 views
27

J'utilise intensivement la validation des annotations de données dans ASP.NET MVC 2. Cette nouvelle fonctionnalité a fait gagner énormément de temps, car je suis maintenant capable de définir la validation côté client et la validation côté serveur en un seul endroit. Cependant, alors que j'effectuais des tests détaillés, j'ai réalisé qu'il était assez facile pour quelqu'un de contourner la validation côté serveur si je m'appuyais uniquement sur la validation des annotations de données. Par exemple, si j'ai défini un champ obligatoire en annotant la propriété avec l'attribut [Obligatoire] et placé une zone de texte pour ce champ obligatoire dans un formulaire, un utilisateur peut simplement supprimer la zone de texte du DOM (qui peut facilement être effectuée via Firebug) et maintenant la validation d'annotation de données ne sera pas déclenchée sur cette propriété pendant ModelBinding à l'intérieur d'un contrôleur. Pour m'assurer que la validation "requise" est déclenchée, je peux répéter la validation après que ModelBinding se soit passé, mais alors je répéterais ma logique de validation.ASP.NET MVC: la validation des annotations de données est-elle suffisante?

Quelle est la recommandation de chacun sur la validation? La validation des annotations de données est-elle suffisante? Ou faut-il répéter la validation pour s'assurer que les validations se déclenchent dans toutes les situations?

Suivi commentaire: Sur la base des réponses ci-dessous, il semble que je ne peux pas compter sur le modèle Binder et la validation des données Annotation seul. Puisque nous concluons qu'une validation supplémentaire côté serveur est requise, y a-t-il un moyen facile pour ma couche Service de déclencher la validation en fonction de ce qui a été défini dans les annotations de données? Il semble que cela nous permettra d'obtenir le meilleur des deux mots ... nous n'aurons pas besoin de répéter le code de validation, mais nous veillerons à ce que la validation soit exécutée même si Model Binder ne le déclenche pas.

Je vais poster ce commentaire de suivi comme une question distincte, car il pose une question différente de celle d'origine.

+0

Réponse Koritnik répond à votre suivi requête. Je fais ma validation similaire à la réponse qu'il a posté. La même définition DataAnnotation peut être utilisée pour la validation du serveur et du client. –

+1

La validation des annotations de données est correcte si les attributs de validation fournis et le cadre lui-même vous conviennent. Le comportement avec Required a été modifié pour ASP.NET MVC 2 RTM en raison des commentaires de la communauté. Par conséquent, [Required] fonctionne désormais comme prévu. Optinalement, consultez: Bloc de validation (bibliothèque d'entreprise), xVal, NHibernate Validators (supposément n'a aucune dépendance sur NHibernate ORM). – miha

+0

'" Je vais publier ce commentaire de suivi sous la forme d'une question distincte, car elle pose une question différente de celle d'origine. "" Un lien vers ça ne serait pas une mauvaise idée, hein? – Sinjai

Répondre

18

Je pense d'être vigilant en ce qui concerne la sécurité, vous devez choisir À vous de faire de la validation du serveur la priorité et assurez-vous que c'est toujours votre solution de repli. Votre validation de serveur devrait fonctionner sans la validation du client. La validation du client est plus pour UX et cela est primordiale pour votre conception, elle est secondaire à la sécurité. Dans cet esprit, vous vous retrouverez à répéter votre validation. Un objectif est souvent d'essayer de concevoir votre application afin que la validation du serveur et du client puisse être intégrée autant que possible afin de réduire le travail requis pour valider sur le serveur et le client. Mais soyez assuré que vous devez faire les deux. Si le contournement de la validation du client (via la manipulation DOM) évite la validation du serveur (ce que vous semblez indiquer), alors la validation de votre serveur pour cette instance risque de ne pas être correctement utilisée. Vous devriez encore appeler votre validation de serveur dans votre action de contrôleur ou dans une couche de service. Le scénario que vous décrivez ne doit pas vaincre la validation de votre serveur.Avec le scénario que vous décrivez, la méthode des attributs DataAnnotation doit être suffisante. Il semble que vous ayez simplement besoin de faire quelques changements de code pour vous assurer que la validation de votre serveur est invoquée également lors de la soumission du formulaire.

2

La DataAnnotation n'est certainement pas suffisante. Je l'utilise également pour pré-valider mes appels au modèle de domaine afin d'obtenir de meilleurs rapports d'erreurs et échouer le plus tôt possible.

Vous pouvez toutefois modifier le modèle DataAnnotation vous-même pour vous assurer que les propriétés avec [Obligatoire] DOIVENT être publiées. (suivra avec le code plus tard aujourd'hui).

MISE À JOUR Retrouve la source pour DataAnnotations Model Binder et trouver cette ligne dans DataAnnotationsModelBinder.cs

// Only bind properties that are part of the request 
if (bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey)) { 

Modifier à

// Only bind properties that are part of the request 
bool contextHasKey = bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey); 
bool isRequired = GetValidationAttributes(propertyDescriptor).OfType<RequiredAttribute>().Count() > 0; 
if (contextHasKey || (!contextHasKey && isRequired)) { 
+0

Merci Martijn. J'ai hâte de voir votre code. –

+0

Bien sûr, je l'ai posté avant de partir au travail (au travail encore maintenant) donc pas le temps de coder :(J'ai modifié le classeur avant car il n'a pas vérifié les objets imbriqués et réinitialiser les propriétés invalides à null que je ne suis pas d'accord http://stackoverflow.com/questions/820468/how-does-dataannotationsmodelbinder-work-with-custom-viewmodels/864541#864541 J'ai ajouté les contrôles requis depuis lors mais je voudrais les tester quand je rentre à la maison avant –

+0

Mis à jour avec le code, mais je ne suis pas en mesure de le tester correctement, je le ferai à nouveau demain mais je l'afficherai donc peut-être que vous pourrez l'évaluer plus rapidement. D: –

7

J'ai couplé xVal avec DataAnnotations et ai écrit mon propre filtre Action qui vérifie tous les paramètres de type Entity à des fins de validation. Donc, si un champ est manquant dans la publication, ce validateur remplira le dictionnaire ModelState et aura donc un modèle invalide.

Pré-requis:

  • mon entité/modèle objets implémentent toutes les interfaces IObjectValidator qui déclare méthode Validate().
  • ma classe d'attribut est appelé ValidateBusinessObjectAttribute
  • bibliothèque validation de xVal

Code du filtre d'action:

public void OnActionExecuting(ActionExecutingContext filterContext) 
{ 
    IEnumerable<KeyValuePair<string, object>> parameters = filterContext.ActionParameters.Where<KeyValuePair<string, object>>(p => p.Value.GetType().Equals(this.ObjectType ?? p.Value.GetType()) && p.Value is IObjectValidator); 
    foreach (KeyValuePair<string, object> param in parameters) 
    { 
     object value; 
     if ((value = param.Value) != null) 
     { 
      IEnumerable<ErrorInfo> errors = ((IObjectValidator)value).Validate(); 
      if (errors.Any()) 
      { 
       new RulesException(errors).AddModelStateErrors(filterContext.Controller.ViewData.ModelState, param.Key); 
      } 
     } 
    } 
} 

Mon action du contrôleur est défini comme celui-ci alors:

[ValidateBusinessObject] 
public ActionResult Register(User user, Company company, RegistrationData registrationData) 
{ 
    if (!this.ModelState.IsValid) 
    { 
     return View(); 
    } 
    ... 
} 
+0

Avez-vous un exemple plus détaillé sur la façon d'utiliser ceci ou un projet téléchargeable peut-être –

+0

@geocine: Où semble être le problème? Utilisez-vous MVC1? Les nouvelles versions n'ont pas besoin de cela, car elles valident automatiquement les paramètres de type fort ... Mais cet exemple est aussi détaillé qu'il devrait l'être dans la réalité. Alors, où semble être le problème? –

+0

Je viens de passer et je suis nouveau à lire aspmvc sur les questions de validation. J'ai oublié que j'utilisais MVC 2. mon mauvais. –

2

j'ai écrit mon propre ValidationService pour MVC 1.0 en copiant des modèles à partir de xV DataAnnotationsRuleProvider de al et DataAnnotationsModelBinder de Microsoft (et les commentaires de Martijn). L'interface de service est ci-dessous:

public interface IValidationService 
{ 
    void Validate(object instance); 

    IEnumerable<ErrorInfo> GetErrors(object instance); 
} 

public abstract class BaseValidationService : IValidationService 
{ 
    public void Validate(object instance) 
    { 
     var errors = GetErrors(instance); 

     if (errors.Any()) 
      throw new RulesException(errors); 
    } 

    public abstract IEnumerable<ErrorInfo> GetErrors(object instance); 
} 

Le service est un coureur de validation qui marche l'arbre de la propriété de l'instance de l'objet qu'il reçoit et exécute en fait la validation des attributs qu'il trouve sur chaque propriété, la construction d'une liste d'objets ERRORINFO lorsque les attributs ne sont pas valides. (Je posterais la source entière mais elle a été écrite pour un client et je ne sais pas encore si je suis autorisé à le faire.)

Vous pouvez ensuite demander à vos contrôleurs, services de logique applicative, d'invoquer explicitement la validation lorsque vous êtes prêt, plutôt que de compter exclusivement sur le modèle de liant pour la validation.

Il y a deux autres pièges que vous devriez être au courant:

  • La valeur par défaut DataTypeAttribute dans les données annotations ne fait réellement aucune validation de type de données , vous aurez donc besoin d'écrire un nouvel attribut que utilise réellement xVal expressions régulières (ou autre chose) à effectuer la validation de type de données côté serveur .
  • xVal ne marche pas propriétés pour créer la validation côté client , vous voudrez peut-être faire quelques changements là-bas pour obtenir plus robuste validation côté client.

Si je suis autorisé et avoir le temps, je vais essayer de rendre plus de source disponible ...

1

Voir CodeProject Server-side Input Validation using Data Annotations

validation d'entrée peut être effectuée automatiquement sur le côté client dans ASP.NET MVC ou valider explicitement le modèle par rapport aux règles. Cette astuce décrira comment cela peut être fait manuellement sur le côté serveur d'une application ASP.NET ou dans le code de dépôt des applications WPF .

 // Use the ValidationContext to validate the Product model against the product data annotations 
     // before saving it to the database 
     var validationContext = new ValidationContext(productViewModel, serviceProvider: null, items:null); 
     var validationResults = new List<ValidationResult>(); 

     var isValid = Validator.TryValidateObject(productViewModel, validationContext,validationResults, true); 
Questions connexes