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?
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
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).
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));
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; }
Vous pouvez désactiver les validateurs conditionnellement en supprimant les erreurs de ModelState:
ModelState["DependentProperty"].Errors.Clear();
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
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.
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));
}
}
}
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.
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.
Utilisation typique pour la suppression conditionnelle de l'erreur de L'État du modèle:
- rendre conditionnelle la première partie de l'action du contrôleur
- effectuez une logique pour supprimer l'erreur de ModelState
- 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.
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.