Wie erhalte ich alle Fehler von ASP.Net MVC modelState?

453

Ich möchte alle Fehlermeldungen aus dem modelState erhalten, ohne die Schlüsselwerte zu kennen. Durchlaufen, um alle Fehlermeldungen abzurufen, die der ModelState enthält.

Wie kann ich das machen?

chobo2
quelle
5
Wenn Sie nur die Fehler @Html.ValidationSummary()anzeigen, können Sie sie schnell und messerscharf anzeigen.
Levininja
11
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }
Razvan Dumitru

Antworten:

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

Siehe auch Wie erhalte ich die Sammlung von Modellstatusfehlern in ASP.NET MVC? .

Oren Trutner
quelle
22
Sehr hilfreich. Beachten Sie, dass in einigen Szenarien, wie z. B. Bindungsfehlern und fehlerhaften Anforderungen, ModelState-Einträge mit leerer Zeichenfolge für Value.ErrorMessageund stattdessen aValue.Exception.Message
AaronLS
5
Fehler sind nett, aber manchmal möchten Sie auch den Schlüssel des Modellzustands (dh den Namen des Feldes). Sie können dies erreichen, indem Sie die erste Zeile in folgende Zeile ändern: foreach (KeyValuePair<string, ModelState> kvp in htmlHelper.ViewData.ModelState) {und diese Zeile darunter einfügen : var modelState = kvp.Value;. Sie können den Schlüssel vonkvp.Key
viggity
533

Verwenden von LINQ :

IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);
mmutilva
quelle
69
Geändert, um IEnumerable <string> nur mit der Fehlermeldung zurückzugeben :: var allErrors = ModelState.Values.SelectMany (v => v.Errors.Select (b => b.ErrorMessage));
Kieran
6
Das ist großartig, aber leider unterstützen Watch / Immediate-Fenster Lambdas nicht :(
AaronLS
3
Ja! Ich (Sie, jeder) brauche "using System.Linq;" oben. Andernfalls erhalten Sie die Meldung "Werte enthält keine Definition für" Viele auswählen "". Es fehlte in meinem Fall.
Estevez
2
warum zum Teufel mit var ?????? Könntest du nicht stattdessen "IEnumerable <ModelError>" schreiben?
Hakan Fıstık
6
@ hakam-fostok @ jb06 ihr habt beide recht. Das Tippen List<string> errors = new List<string>()statt var errors = new List<string>()ist wirklich Zeitverschwendung, aber das Schreiben IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);, bei dem der Rückgabetyp nicht wirklich klar ist, ist in Bezug auf die Lesbarkeit wirklich besser. (Auch wenn Visual Studio es Ihnen mit dem Mauszeiger geben kann)
aprovent
192

Aufbauend auf der LINQ-Version, wenn Sie alle Fehlermeldungen zu einer Zeichenfolge zusammenfassen möchten:

string messages = string.Join("; ", ModelState.Values
                                        .SelectMany(x => x.Errors)
                                        .Select(x => x.ErrorMessage));
Dunc
quelle
5
Die andere Option besteht darin, Folgendes zu tun: ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .JoinString (";");
Tod Thomson
3
@Tod, ist IEnumerable.JoinString () Ihre eigene Erweiterungsmethode? Siehe stackoverflow.com/q/4382034/188926
Dunc
2
Hey Dunc - ja, ich vermute, ich habe diese Erweiterungsmethode zu meiner Codebasis hinzugefügt und sie vergessen und dann gedacht, es sei eine Framework-Methode. LOL :(
Tod Thomson
5
oder ... ModelState.Values.SelectMany (O => O.Errors) .Select (O => O.ErrorMessage) .Aggregate ((U, V) => U + "," + V)
fordareh
2
Dies funktioniert hervorragend, wenn Sie eine Web-API verwenden und ein IHttpActionResult-Ergebnis zurückgeben. Sie können also einfach Folgendes tun: BadRequest (Nachrichten) zurückgeben; Danke, Dunc!
Rich Ward
32

Ich konnte dies mit einem kleinen LINQ tun,

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;
}

Die obige Methode gibt eine Liste von Validierungsfehlern zurück.

Weiterführende Literatur:

Lesen aller Fehler aus ModelState in ASP.NET MVC

Yasser Shaikh
quelle
17

Während des Debuggens finde ich es nützlich, am Ende jeder meiner Seiten eine Tabelle einzufügen, um alle ModelState-Fehler anzuzeigen.

<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
quelle
Wenn es hier
Randfälle
12

Wie ich festgestellt habe, können Sie Ausnahmen befolgen, ohne dass Fehlermeldungen festgelegt wurden. Wenn Sie also alle Probleme abfangen, die wirklich erforderlich sind, müssen Sie sowohl die ErrorMessage als auch die Exception abrufen.

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

oder als Erweiterungsmethode

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

}
Alan Macdonald
quelle
Warum möchten Sie eine Zeichenfolge mit allen Fehlern? macht keinen Sinn, wenn Sie etwas damit in der Ansicht machen wollen, eine Reihe von Listen ist viel besser imho
Daniël Tulp
1
Debuggen. Mein erstes Problem war herauszufinden, was mit meiner App falsch lief. Ich habe nicht versucht, dem Benutzer zu sagen, dass er nur herausgefunden hat, was schief gelaufen ist. Außerdem ist es trivial, dieses Beispiel von der Erstellung einer Aufzählung von Zeichenfolgen in eine Aufzählung von etwas anderem umzuwandeln, z. B. Fehlermeldung und Ausnahme, sodass das wirklich Nützliche darin besteht, zu wissen, dass Sie beide Informationen benötigen
Alan Macdonald,
Übrigens, Sie haben erkannt, dass die zweite Erweiterungsmethode IEnumerable <String> zurückgibt und nicht nur eine große einzelne Zeichenfolge?
Alan Macdonald
8

Falls jemand die Eigenschaft Name des Modells zurückgeben möchte, um die Fehlermeldung in einer stark typisierten Ansicht zu binden.

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);
    }
}

Auf diese Weise können Sie den Fehler tatsächlich mit dem Feld verknüpfen, das den Fehler ausgelöst hat.

james31rock
quelle
7

Es reichte mir nicht aus, nur die Fehlermeldungen selbst auszugeben, aber das war der Trick.

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
quelle
1
Als Warnung können die Schlüsselwertpaare in ModelState NULL-Werte enthalten, weshalb der ursprüngliche Code hier ein niedliches C # 6-Geschäft mit einem Null-Koaleszenz-Operator (?.) Enthält, daher das Currying zum ?? am Ende des Ausdrucks. Der ursprüngliche Ausdruck, der vor Nullfehlern schützen sollte, war: state.Value.?AttemptedValue ?? "[NULL]". Soweit ich weiß, ist der Code in seinem aktuellen Zustand ohne die hinterhältige Behandlung von Fällen, in denen state.Value == null ist, gefährdet.
Josh Sutterfield
5

Für den Fall, dass jemand es braucht, habe ich die folgende statische Klasse in meinen Projekten erstellt und verwendet

Anwendungsbeispiel:

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

Verwendung:

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

Klasse:

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
quelle
Danke CodeArtist !! Ich habe den Code unterhalb seiner Implementierung geringfügig geändert.
Alfred Severo
4

Und das funktioniert auch:

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
quelle
@Yasser Hast du Totos Antwort gesehen?
Der Muffin-Mann
@ TheMuffinMan ja ich habe. Was ist damit ?
Yasser Shaikh
@ Yasser Es ist die beste Antwort. Daran ist nichts auszusetzen, aber es macht keinen Sinn, ihn zu verwenden, wenn er SelectManyverfügbar ist.
Der Muffin-Mann
4

Nützlich zum Übergeben einer Reihe von Fehlermeldungen an View, möglicherweise über Json:

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

Dies erweitert die Antwort von @Dunc. Siehe XML-Dokumentkommentare

// 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
quelle
3

Darüber hinaus ModelState.Values.ErrorMessagekann leer sein, ModelState.Values.Exception.Messagekann aber auf einen Fehler hinweisen.

Jason Dufair
quelle
0

In Ihrer Implementierung fehlt Ihnen die statische Klasse, dies sollte sein.

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

lieber

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

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

Verwenden Sie einfach den Asp-Validation-Summary Tag Helper

Armin Azhdari
quelle