ASP.NET validation conditionnelle MVC

Comment utiliser les annotations de données pour effectuer une validation conditionnelle sur le modèle?

Par exemple, disons que nous avons le modèle suivant (Person et Senior):

public class Person
{
    [Required(ErrorMessage = "*")]
    public string Name
    {
        get;
        set;
    }

    public bool IsSenior
    {
        get;
        set;
    }

    public Senior Senior
    {
        get;
        set;
    }
}

public class Senior
{
    [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
    public string Description
    {
        get;
        set;
    }
}

Et la vue suivante:

<%= Html.EditorFor(m => m.Name)%>
<%= Html.ValidationMessageFor(m => m.Name)%>

<%= Html.CheckBoxFor(m => m.IsSenior)%>
<%= Html.ValidationMessageFor(m => m.IsSenior)%>

<%= Html.CheckBoxFor(m => m.Senior.Description)%>
<%= Html.ValidationMessageFor(m => m.Senior.Description)%>

Je voudrais être le "Principal.Description "propriété conditionnel champ obligatoire basé sur la sélection de la propriété" IsSenior " (true -> required). Comment implémenter la validation conditionnelle dans ASP.NET MVC 2 avec des annotations de données?

116
demandé sur Andrew Arnold 2010-03-10 16:19:44

12 réponses

Il existe une bien meilleure façon d'ajouter des règles de validation conditionnelle dans MVC3. Demandez à votre modèle d'hériter D'IValidatableObject et implémentez la méthode Validate:

public class Person : IValidatableObject
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
        if (IsSenior && string.IsNullOrEmpty(Senior.Description)) 
            yield return new ValidationResult("Description must be supplied.");
    }
}

Voir plus d'une description à http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

138
répondu viperguynaz 2015-05-27 10:13:31

J'ai résolu cela en manipulant le dictionnaire "ModelState" , qui est contenu par le contrôleur. Le dictionnaire ModelState inclut tous les membres qui doivent être validés.

Voici la solution:

Si vous devez implémenter une validation conditionnelle basée sur un champ (par exemple si a = true, alors B est requis), tout en maintenant la messagerie d'erreur au niveau de la propriété (ce n'est pas vrai pour les validateurs personnalisés au niveau de l'objet) vous pouvez y parvenir en manipulant "ModelState", en supprimant simplement les validations indésirables de celui-ci.

...dans une classe...

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

[Required(ErrorMessage = "*")] 
public string DepentedProperty
{
  get;
  set;
}

...classe continue...

...dans une action de contrôleur ...

if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
   this.ModelState.Remove("DepentedProperty");
}

...

Avec cela, nous obtenons une validation conditionnelle, tout en laissant tout le reste pareil.


Mise à jour:

Ceci est mon implémentation finale: j'ai utilisé une interface sur le modèle et l'attribut action qui valide le modèle qui implémente ladite interface. Interface prescrit la méthode Validate (Modelstaedictionary modelState). L'attribut on action appelle simplement Validate (modelState) sur IValidatorSomething.

Je ne voulais pas compliquer cette réponse, donc je n'ai pas mentionné les détails d'implémentation finaux (qui, à la fin, comptent dans le code de production).

60
répondu Peter Stegnar 2016-12-14 10:54:48

J'ai eu le même problème hier mais je l'ai fait d'une manière très propre qui fonctionne pour la validation côté client et Côté Serveur.

Condition: en fonction de la valeur d'une autre propriété dans le modèle, vous souhaitez rendre une autre propriété requise. Voici le code

public class RequiredIfAttribute : RequiredAttribute
{
    private String PropertyName { get; set; }
    private Object DesiredValue { get; set; }

    public RequiredIfAttribute(String propertyName, Object desiredvalue)
    {
        PropertyName = propertyName;
        DesiredValue = desiredvalue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        Object instance = context.ObjectInstance;
        Type type = instance.GetType();
        Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
        if (proprtyvalue.ToString() == DesiredValue.ToString())
        {
            ValidationResult result = base.IsValid(value, context);
            return result;
        }
        return ValidationResult.Success;
    }
}

Ici PropertyName est la propriété sur laquelle vous voulez faire votre condition DesiredValue est la valeur particulière de PropertyName (propriété) pour laquelle votre autre propriété doit être validée requis

Dites que vous avez ce qui suit

public class User
{
    public UserType UserType { get; set; }

    [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
    public string Password
    {
        get;
        set;
    }
}

Enfin , mais pas le moindre, enregistrez l'adaptateur pour votre attribut afin qu'il puisse faire la validation côté client (je l'ai mis en global.asax, Application_Start)

 DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
33
répondu Dan Hunex 2013-05-11 11:59:54

J'ai utilisé cet incroyable nuget qui fait des annotations dynamiques ExpressiveAnnotations

Vous pouvez valider toute logique dont vous pouvez rêver:

public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }
26
répondu Korayem 2016-08-31 09:24:41

Vous pouvez désactiver les validateurs conditionnellement en supprimant les erreurs de ModelState:

ModelState["DependentProperty"].Errors.Clear();
17
répondu Pavel Chuchuva 2010-10-24 22:39:37

Merci Merritt :)

Je viens de mettre à jour ceci en MVC 3 au cas où quelqu'un le trouverait utile; http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx

Simon

8
répondu Simon Ince 2011-02-06 18:10:31

Il y a maintenant un framework qui fait cette validation conditionnelle (entre autres validations d'annotation de données pratiques) hors de la boîte: http://foolproof.codeplex.com/

Plus précisément, jetez un oeil au validateur [RequiredIfTrue("IsSenior")]. Vous mettez cela directement sur la propriété que vous souhaitez valider, de sorte que vous obtenez le comportement souhaité de l'erreur de validation associée à la propriété "Senior".

Il est disponible en tant que paquet NuGet.

6
répondu bojingo 2013-12-04 15:44:14

Vous devez valider au niveau de la personne, pas au niveau supérieur, ou Senior doit avoir une référence à sa personne parente. Il me semble que vous avez besoin d'un mécanisme d'auto-validation qui définit la validation sur la personne et non sur l'une de ses propriétés. Je ne suis pas sûr, mais je ne pense pas que DataAnnotations supporte cela hors de la boîte. Ce que vous pouvez faire créez votre propre {[1] } qui dérive de ValidationAttribute qui peut être décoré au niveau de la classe et ensuite créez un validateur personnalisé qui permet également validateurs de niveau classe à exécuter.

Je sais que le bloc D'Application de Validation prend en charge l'auto-validation prête à l'emploi, mais VAB a une courbe d'apprentissage assez raide. Néanmoins, voici un exemple utilisant VAB:

[HasSelfValidation]
public class Person
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    [SelfValidation]
    public void ValidateRange(ValidationResults results)
    {
        if (this.IsSenior && this.Senior != null && 
            string.IsNullOrEmpty(this.Senior.Description))
        {
            results.AddResult(new ValidationResult(
                "A senior description is required", 
                this, "", "", null));
        }
    }
}
3
répondu Steven 2010-03-10 16:40:37

J'ai eu le même problème, j'avais besoin d'une modification du champ attribute - make [Required] requis en dépendance de la requête http.La solution était similaire à la réponse de Dan Hunex, mais sa solution ne fonctionnait pas correctement (voir les commentaires). Je n'utilise pas de validation discrète, juste MicrosoftMvcValidation.js hors de la boîte. Elle est ici. Implémentez votre attribut personnalisé:

public class RequiredIfAttribute : RequiredAttribute
{

    public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/)
    {

    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {

    //You can put your logic here   

        return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need
    }


}

Ensuite, vous devez implémenter votre fournisseur personnalisé pour l'utiliser comme adaptateur dans votre global.asax

public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute>
{

    ControllerContext ccontext;
    public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
       : base(metadata, context, attribute)
    {
        ccontext = context;// I need only http request
    }

//override it for custom client-side validation 
     public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
     {       
               //here you can customize it as you want
         ModelClientValidationRule rule = new ModelClientValidationRule()
         {
             ErrorMessage = ErrorMessage,
    //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"    
             ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none";
         };
         return new ModelClientValidationRule[] { rule };
      }
}

Et modifier votre mondial.asax avec une ligne

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));

Et voilà

[RequiredIf]
public string NomenclatureId { get; set; }

Le principal avantage pour moi est que je n'ai pas besoin de coder un validateur client personnalisé comme dans le cas d'une validation discrète. cela fonctionne comme [requis], mais seulement dans les cas que vous voulez.

3
répondu Den 2014-03-06 09:20:52

Découvrez ce gars:

Http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx

Je travaille à travers son exemple de projet en ce moment.

2
répondu Merritt 2010-10-25 20:52:27

Utilisation typique pour la suppression conditionnelle de l'erreur de L'État du modèle:

  1. rendre conditionnelle la première partie de l'action du contrôleur
  2. effectuez une logique pour supprimer l'erreur de ModelState
  3. Faites le reste de la logique existante (généralement la validation de L'État du modèle, puis tout le reste)

Exemple:

public ActionResult MyAction(MyViewModel vm)
{
    // perform conditional test
    // if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")

    // Do typical model state validation, inside following if:
    //     if (!ModelState.IsValid)

    // Do rest of logic (e.g. fetching, saving

Dans votre exemple, gardez tout tel quel et ajoutez la logique suggérée à l'Action de votre contrôleur. Je suppose que votre ViewModel est passé à l'action du contrôleur a les objets Person et Senior Person avec des données remplies à partir de l'interface utilisateur.

0
répondu Jeremy Ray Brown 2015-08-09 20:10:36

J'utilise MVC 5 mais vous pouvez essayer quelque chose comme ceci:

public DateTime JobStart { get; set; }

[AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")]
[DisplayName("Start Date")]
[Required]
public DateTime? StartDate { get; set; }

Dans votre cas, vous diriez quelque chose comme "IsSenior = = true". Ensuite, vous avez juste besoin de vérifier la validation de votre action post.

0
répondu fosterImposter 2017-01-06 18:37:07