Comment obtenir toutes les erreurs de ASP.Net MVC modelState?

453

Je veux extraire tous les messages d'erreur de modelState sans connaître les valeurs clés. En boucle pour récupérer tous les messages d'erreur que contient le ModelState.

Comment puis-je faire ceci?

chobo2
la source
5
Si vous affichez simplement les erreurs, @Html.ValidationSummary()c'est un moyen rapide de les afficher toutes dans le rasoir.
levininja
11
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }
Razvan Dumitru

Réponses:

531
foreach (ModelState modelState in ViewData.ModelState.Values) {
    foreach (ModelError error in modelState.Errors) {
        DoSomethingWith(error);
    }
}

Voir aussi Comment obtenir la collection des erreurs d'état du modèle dans ASP.NET MVC? .

Oren Trutner
la source
22
Très utile. Remarque dans certains scénarios, tels que les échecs de liaison et les mauvaises demandes, il y aura des entrées ModelState avec une chaîne vide pour Value.ErrorMessageet à la place unValue.Exception.Message
AaronLS
5
Les erreurs sont agréables mais parfois vous voulez aussi la clé de l'état du modèle (c'est-à-dire le nom du champ). vous pouvez obtenir qu'en changeant la première ligne à ceci: foreach (KeyValuePair<string, ModelState> kvp in htmlHelper.ViewData.ModelState) {et insérer cette ligne en dessous: var modelState = kvp.Value;. Vous pouvez obtenir la clé dekvp.Key
viggity
534

Utilisation de LINQ :

IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);
mmutilva
la source
69
Modifié pour renvoyer IEnumerable <string> avec juste le message d'erreur :: var allErrors = ModelState.Values.SelectMany (v => v.Errors.Select (b => b.ErrorMessage));
Kieran
6
C'est super, mais malheureusement, les fenêtres Watch / Immediate ne prennent pas en charge les lambda :(
AaronLS
3
Oui! J'ai (vous, n'importe qui) besoin "d'utiliser System.Linq;" au sommet. Sinon, vous obtenez le message «Les valeurs ne contiennent pas de définition pour Sélectionner plusieurs». Il manquait dans mon cas.
Estevez
2
pourquoi diable utiliser de var ?????? ne pourriez-vous pas écrire «IEnumerable <ModelError>» à la place ???
Hakan Fıstık
6
@ hakam-fostok @ jb06 vous avez tous les deux raison. Taper List<string> errors = new List<string>()au lieu de var errors = new List<string>()est vraiment une perte de temps, mais l'écriture IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);, où le type de retour n'est pas vraiment clair, est vraiment plus grande en termes de lisibilité. (même si Visual Studio peut vous le donner au
survol de la
192

En s'appuyant sur la version LINQ, si vous souhaitez joindre tous les messages d'erreur en une seule chaîne:

string messages = string.Join("; ", ModelState.Values
                                        .SelectMany(x => x.Errors)
                                        .Select(x => x.ErrorMessage));
Dunc
la source
5
L'autre option consiste à effectuer les opérations suivantes: ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .JoinString (";");
Tod Thomson
3
@Tod, IEnumerable.JoinString () est-il votre propre méthode d'extension? Voir stackoverflow.com/q/4382034/188926
Dunc
2
Hé Dunc - oui je soupçonne que j'ai ajouté cette méthode d'extension à ma base de code et que je l'ai oubliée et que je pensais que c'était une méthode cadre LOL :(
Tod Thomson
5
ou ... ModelState.Values.SelectMany (O => O.Errors) .Select (O => O.ErrorMessage) .Aggregate ((U, V) => U + "," + V)
fordareh
2
Cela fonctionne très bien lorsque vous utilisez l'API Web et renvoyez un résultat IHttpActionResult. Ainsi, vous pouvez simplement faire: renvoyer BadRequest (messages); Merci, Dunc!
Rich Ward
32

J'ai pu le faire en utilisant un petit LINQ,

public static List<string> GetErrorListFromModelState
                                              (ModelStateDictionary modelState)
{
      var query = from state in modelState.Values
                  from error in state.Errors
                  select error.ErrorMessage;

      var errorList = query.ToList();
      return errorList;
}

La méthode ci-dessus renvoie une liste d'erreurs de validation.

Lectures complémentaires:

Comment lire toutes les erreurs de ModelState dans ASP.NET MVC

Yasser Shaikh
la source
17

Pendant le débogage, je trouve utile de mettre un tableau au bas de chacune de mes pages pour montrer toutes les erreurs ModelState.

<table class="model-state">
    @foreach (var item in ViewContext.ViewData.ModelState) 
    {
        if (item.Value.Errors.Any())
        { 
        <tr>
            <td><b>@item.Key</b></td>
            <td>@((item.Value == null || item.Value.Value == null) ? "<null>" : item.Value.Value.RawValue)</td>
            <td>@(string.Join("; ", item.Value.Errors.Select(x => x.ErrorMessage)))</td>
        </tr>
        }
    }
</table>

<style>
    table.model-state
    {
        border-color: #600;
        border-width: 0 0 1px 1px;
        border-style: solid;
        border-collapse: collapse;
        font-size: .8em;
        font-family: arial;
    }

    table.model-state td
    {
        border-color: #600;
        border-width: 1px 1px 0 0;
        border-style: solid;
        margin: 0;
        padding: .25em .75em;
        background-color: #FFC;
    }
 </style>
Simon_Weaver
la source
s'il y a des cas limites ici où cela échoue, veuillez simplement modifier la réponse pour le corriger
Simon_Weaver
12

Comme j'ai découvert avoir suivi les conseils des réponses données jusqu'à présent, vous pouvez obtenir des exceptions sans que des messages d'erreur ne soient définis.Par conséquent, pour détecter tous les problèmes, vous devez vraiment obtenir à la fois le message d'erreur et l'exception.

String messages = String.Join(Environment.NewLine, ModelState.Values.SelectMany(v => v.Errors)
                                                           .Select( v => v.ErrorMessage + " " + v.Exception));

ou comme méthode d'extension

public static IEnumerable<String> GetErrors(this ModelStateDictionary modelState)
{
      return modelState.Values.SelectMany(v => v.Errors)
                              .Select( v => v.ErrorMessage + " " + v.Exception).ToList();

}
Alan Macdonald
la source
pourquoi voudriez-vous une chaîne avec toutes les erreurs? n'a pas de sens lorsque vous voulez faire quelque chose avec cela dans la vue, un tableau de liste est bien mieux à mon humble avis
Daniël Tulp
1
Déboguer. Mon premier problème a été de découvrir ce qui n'allait pas avec mon application. Je n'essayais pas de dire à l'utilisateur de découvrir ce qui n'allait pas. En outre, il est trivial de convertir cet exemple de la création d'une énumération de chaînes en une énumération d'autre chose, par exemple un message d'erreur et une exception, donc la chose vraiment utile est de savoir que vous avez besoin des deux bits d'informations
Alan Macdonald
BTW vous avez réalisé que la deuxième méthode d'extension renvoie IEnumerable <String> et pas seulement une grosse chaîne unique?
Alan Macdonald
8

Si quelqu'un souhaite renvoyer le nom de la propriété Model pour lier le message d'erreur dans une vue fortement typée.

List<ErrorResult> Errors = new List<ErrorResult>();
foreach (KeyValuePair<string, ModelState> modelStateDD in ViewData.ModelState)
{
    string key = modelStateDD.Key;
    ModelState modelState = modelStateDD.Value;

    foreach (ModelError error in modelState.Errors)
    {
        ErrorResult er = new ErrorResult();
        er.ErrorMessage = error.ErrorMessage;
        er.Field = key;
        Errors.Add(er);
    }
}

De cette façon, vous pouvez réellement lier l'erreur au champ qui a provoqué l'erreur.

james31rock
la source
7

Produire uniquement les messages d'erreur eux-mêmes n'était pas suffisant pour moi, mais cela a fait l'affaire.

var modelQuery = (from kvp in ModelState
                  let field = kvp.Key
                  let state = kvp.Value
                  where state.Errors.Count > 0
                  let val = state.Value?.AttemptedValue ?? "[NULL]"

                  let errors = string.Join(";", state.Errors.Select(err => err.ErrorMessage))
                  select string.Format("{0}:[{1}] (ERRORS: {2})", field, val, errors));

Trace.WriteLine(string.Join(Environment.NewLine, modelQuery));
Josh Sutterfield
la source
1
À titre d'avertissement, les paires de valeurs clés dans ModelState peuvent inclure des valeurs NULL, c'est pourquoi le code d'origine ici incluait des affaires C # 6 mignonnes avec un opérateur null-coalesce (?.), D'où le currying vers le ?? à la fin de l'expression. L'expression d'origine qui devrait protéger des erreurs nulles était: state.Value.?AttemptedValue ?? "[NUL]". Pour autant que je sache, le code dans son état actuel, sans la manipulation sournoise des cas où state.Value == null, est en danger.
Josh Sutterfield
5

Pour juste au cas où quelqu'un en aurait besoin, j'ai créé et utilise la classe statique suivante dans mes projets

Exemple d'utilisation:

if (!ModelState.IsValid)
{
    var errors = ModelState.GetModelErrors();
    return Json(new { errors });
}

Utilisations:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using WebGrease.Css.Extensions;

Classe:

public static class ModelStateErrorHandler
{
    /// <summary>
    /// Returns a Key/Value pair with all the errors in the model
    /// according to the data annotation properties.
    /// </summary>
    /// <param name="errDictionary"></param>
    /// <returns>
    /// Key: Name of the property
    /// Value: The error message returned from data annotation
    /// </returns>
    public static Dictionary<string, string> GetModelErrors(this ModelStateDictionary errDictionary)
    {
        var errors = new Dictionary<string, string>();
        errDictionary.Where(k => k.Value.Errors.Count > 0).ForEach(i =>
        {
            var er = string.Join(", ", i.Value.Errors.Select(e => e.ErrorMessage).ToArray());
            errors.Add(i.Key, er);
        });
        return errors;
    }

    public static string StringifyModelErrors(this ModelStateDictionary errDictionary)
    {
        var errorsBuilder = new StringBuilder();
        var errors = errDictionary.GetModelErrors();
        errors.ForEach(key => errorsBuilder.AppendFormat("{0}: {1} -", key.Key,key.Value));
        return errorsBuilder.ToString();
    }
}
CodeArtist
la source
Merci CodeArtist !! J'ai fait un petit changement dans le code sous son implémentation.
Alfred Severo
4

Et cela fonctionne aussi:

var query = from state in ModelState.Values
    from error in state.Errors
    select error.ErrorMessage;
var errors = query.ToArray(); // ToList() and so on...
ravy amiry
la source
@Yasser Avez-vous vu la réponse de Toto?
The Muffin Man
@TheMuffinMan oui j'ai. Qu'en est-il?
Yasser Shaikh
@Yasser C'est la meilleure réponse. Rien de mal à celui-ci, mais inutile de l'utiliser quand il SelectManyest disponible.
The Muffin Man
4

Utile pour transmettre un tableau de messages d'erreur à View, peut-être via Json:

messageArray = this.ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors, (modelState, error) => error.ErrorMessage).ToArray();
Steve Lydford
la source
4

Cela élargit la réponse de @Dunc. Voir les commentaires de doc xml

// ReSharper disable CheckNamespace
using System.Linq;
using System.Web.Mvc;


public static class Debugg
{
    /// <summary>
    /// This class is for debugging ModelState errors either in the quick watch 
    /// window or the immediate window.
    /// When the model state contains dozens and dozens of properties, 
    /// it is impossible to inspect why a model state is invalid.
    /// This method will pull up the errors
    /// </summary>
    /// <param name="modelState">modelState</param>
    /// <returns></returns>
    public static ModelError[]  It(ModelStateDictionary modelState)
    {
        var errors = modelState.Values.SelectMany(x => x.Errors).ToArray();
        return errors;            
    }
}
Chris Marisic
la source
3

De plus, ModelState.Values.ErrorMessagepeut être vide, mais ModelState.Values.Exception.Messagepeut indiquer une erreur.

Jason Dufair
la source
0

Dans votre implémentation, il vous manque la classe statique, ce devrait être le cas.

if (!ModelState.IsValid)
{
    var errors =  ModelStateErrorHandler.GetModelErrors(this.ModelState);
    return Json(new { errors });
}

plutôt

if (!ModelState.IsValid)
{
    var errors = ModelState.GetModelErrors();
    return Json(new { errors });
}
Alfred Severo
la source
0

<div class="text-danger" style="direction:rtl" asp-validation-summary="All"></div>

utilisez simplement asp-validation-summary Tag Helper

Armin Azhdari
la source