web-dev-qa-db-fra.com

Est-il possible d'ignorer certaines propriétés (sur un POCO) lors de la validation d'un formulaire dans ASP.NET MVC3?

j'ai un assistant d'inscription pour l'enregistrement de nouveaux utilisateurs. Lorsque j'essaie de passer à la 2ème page, des erreurs de validation surviennent car mon objet User n'a pas encore été entièrement rempli. Existe-t-il un moyen de dire à chaque ActionMethod d'ignorer certaines propriétés lorsqu'il vérifie les vérifications ModelState.IsValid?

par exemple. (Code pseduo simplifié)

public class User
{
   [Required]
   public string Name; // Asked on page 1.
   [Required]
   public int Age; // Asked on page 1.
   [Required]
   public string Avatar;  // Asked on Page 2.
}

il se plaint en disant que l'avatar est requis/ne peut pas être nul. Mais je n'ai pas l'occasion de demander à l'utilisateur de remplir ceci, jusqu'à la page suivante.

Est-il possible de demander d'ignorer cette vérification, à la page 1?

19
Pure.Krome
19
Linkgoron

Dans l'action, supprimez simplement les erreurs pour les éléments non encore vérifiés. Cela rend alors votre modèle valide pour les éléments déjà cochés

foreach (var error in ModelState["Avatar"].Errors)
 {
      ModelState["Avatar"].Errors.Remove(error);
 }

ou

ModelState["Avatar"].Errors.Clear();
19
Darroll

Ceci est discuté dans le livre asp.net mvc 2 de Steve Sanderson, page 486.

Créez un attribut personnalisé, ValidateIncomingValuesOnlyAttribute, qui hérite d'ActionFilterAttribute, et appliquez-le à votre classe de contrôleur.

Remplacez la méthode OnActionExecuting:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{

var modelState = filterContext.Controller.ViewData.ModelState;
var incomingValues = filterContext.Controller.ValueProvider;

var keys = modelState.Keys.Where(x => !incomingValues.ContainsPrefix(x));
foreach(var key in keys)
{
modelState[key].Errors.Clear();
}
}

De cette façon, vous validez les données qui se rapportent à chaque étape de l’Assistant uniquement. Vous avez ensuite besoin d'une page de confirmation sans entrée de données pour envoyer les données validées au serveur.

Mais surtout, lisez le livre de Steve Sanderson, il donne une solution de travail à cela et à votre autre problème.

Addenda:

Si, au lieu de ce qui précède, vous décidez de mapper un modèle de vue, faites attention, car vous devrez soit:

une. Décorez pas les propriétés du modèle de vue avec des attributs de validation dataannotation. Dans ce cas, vous ne validez que lorsque l'utilisateur a rempli l'assistant complet et que vous essayez de le soumettre à la base de données. Ce serait très ennuyeux du point de vue de l'utilisateur ...

b. Sinon, vous devez toujours utiliser la technique décrite par S Sanderson, c'est-à-dire effacer toutes les erreurs de validation qui ne concernent pas les champs de l'étape en cours.

Je ne vois pas la réponse acceptée comme répondant à la question posée.

5
awrigley

Pour ignorer les propriétés de ModelState, voici le code le plus simple.

if (ModelState["PropertyName"] != null) ModelState["PropertyName"].Errors.Clear();
3
Shyam Bhagat

Je ne faisais que manipuler les formulaires de validation et ModelState et ai découvert une solution très simple à votre problème sans écrire de nouvelle méthode, de substitution, etc. 

ModelState.Where(m => m.Key == "Avatar").FirstOrDefault().Value.Errors.Clear();
// At  this point ModeState will have an error for that Key,
// by applying Clear it remove the error so modelstate becomes valid again

if (!ModelState.IsValid) {
    return View("User", model);
} else {     
    try  {
        // do something
    } catch {
        TempData["errorMessage"] = "something went wrong";
    }
}
2
Adam Bielecki
public override void OnActionExecuting(ActionExecutingContext context)
{
    var modelstate = context.ModelState;
    var keys = modelstate.Keys.Where(x => ExculdeFeilds.Split(",").ToList().Contains(x));
    foreach (var item in keys)
    {
        modelstate[item].ValidationState = ModelValidationState.Valid;
    }
    if (!modelstate.IsValid)
    {
        context.Result = new BadRequestObjectResult(context.ModelState);
    }
}
0
Varun Anand

Les modèles de visualisation qui correspondent exactement aux données en cours de réinscription constituent généralement la technique recommandée, car ils sont très prévisibles et que vous bénéficiez de tous les avantages d'un dactylographie, d'un échafaudage rigoureux, etc. ne pas être réintégré dans le compte et peut entraîner une défaillance silencieuse au moment de l'exécution lorsqu'un nom de propriété est modifié, mais que les chaînes Inclure ou Exclure de BindAttribute ne le sont pas. Éviter l’utilisation d’attributs de validation présente de nombreux inconvénients dans MVC et doit être remplacé par une autre technique de validation telle que IValidatableObject ou FluentValidation.

Malgré tous les avantages des ViewModels et les mises en garde qui accompagnent BindAttribute, il peut parfois être préférable d’utiliser BindAttribute et de l’envoyer partiellement à un modèle/viewmodel. Cet ActionFilterAttribute couvre ce cas précis. Le code @awrigley cité est un peu plus loin, mais au lieu d'effacer les erreurs basées sur ValueProvider, il supprime les erreurs basées sur l'utilisation de BindAttribute (par exemple, Include et Exclude). Cet attribut peut être ajouté en toute sécurité à GlobalFilterCollection car il ne modifiera pas le comportement de la validation MVC lorsque l'attribut BindAttribute n'a pas été appliqué. Remarque: je ne l'ai pas beaucoup utilisée, mais cela fonctionne bien pour mes cas de base.

using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;

/// <summary>
/// When the BindAttribute is in use, validation errors only show for values that 
/// are included or not excluded.
/// </summary>
public class ValidateBindableValuesOnlyAttributes : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var modelState = filterContext.Controller.ViewData.ModelState;
        var includedProperties = filterContext.ActionDescriptor.GetParameters()
            .SelectMany(o => o.BindingInfo.Include.Select(name => (string.IsNullOrWhiteSpace(o.BindingInfo.Prefix) ? "" : o.BindingInfo.Prefix + ".") + name));
        var excludedProperties = filterContext.ActionDescriptor.GetParameters()
            .SelectMany(o => o.BindingInfo.Exclude.Select(name => (string.IsNullOrWhiteSpace(o.BindingInfo.Prefix) ? "" : o.BindingInfo.Prefix + ".") + name));

        var ignoreTheseProperties = new List<KeyValuePair<string, ModelState>>();
        if (includedProperties.Any())
        {
            ignoreTheseProperties.AddRange(modelState.Where(k => !includedProperties.Any(name => Regex.IsMatch(k.Key, "^" + Regex.Escape(name) + @"(\.|\[|$)"))));
        }
        ignoreTheseProperties.AddRange(modelState.Where(k => excludedProperties.Any(name => Regex.IsMatch(k.Key, "^" + Regex.Escape(name) + @"(\.|\[|$)"))));

        foreach (var item in ignoreTheseProperties)
        {
            item.Value.Errors.Clear();
        }
    }
}
0
Jeremy Cook

Qu'en est-il de la classe personnalisée IgnoreModelErrors?

http://mrbigglesworth79.blogspot.in/2011/12/partial-validation-with-data.html


Héritez de la classe ActionFilterAttribute et effacez les erreurs [en fonction de noms ou de motifs de regex correspondants] dans OnActionExecuting, comme indiqué dans le lien ci-dessus. Ce sera plus propre.

0
Hoven

J'avais une entité de référence qui n'était pas supposée être validée.

Retiré de la validation au début de l'action:

[HttpPost]
public async Task<IActionResult> Post([FromBody] Contact contact)
{
  var skipped = ModelState.Keys.Where(key => key.StartsWith(nameof(Contact.Portfolios)));
  foreach (var key in skipped)
    ModelState.Remove(key);
    //ModelState doesn't include anything about Portfolios which we're not concerned with

  if (!ModelState.IsValid)
    return BadRequest(ModelState);

  //Rest of action
}
0
Shimmy