Jak uzyskać wszystkie błędy z ASP.Net MVC modelState?

453

Chcę uzyskać wszystkie komunikaty o błędach ze modelu modelState bez znajomości kluczowych wartości. Pętla przechwytuje wszystkie komunikaty o błędach, które zawiera ModelState.

W jaki sposób mogę to zrobić?

chobo2
źródło
5
Jeśli wyświetlasz tylko błędy, możesz @Html.ValidationSummary()szybko wyświetlić je wszystkie.
levininja
11
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }
Razvan Dumitru

Odpowiedzi:

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

Zobacz także Jak uzyskać kolekcję błędów stanu modelu w ASP.NET MVC? .

Oren Trutner
źródło
22
Bardzo pomocny. Uwaga: w niektórych scenariuszach, takich jak niepowodzenia wiązania i złe żądania, pojawią się wpisy ModelState z pustym ciągiem znaków Value.ErrorMessagezamiastValue.Exception.Message
AaronLS
5
Błędy są miłe, ale czasem potrzebujesz też klucza stanu modelu (tj. Nazwy pola). że można uzyskać zmieniając pierwszą linię do tego: foreach (KeyValuePair<string, ModelState> kvp in htmlHelper.ViewData.ModelState) {i wstawić ten wiersz pod nim: var modelState = kvp.Value;. Klucz możesz zdobyć odkvp.Key
viggity
534

Za pomocą LINQ :

IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);
mmutilva
źródło
69
Zmodyfikowano, aby zwracał IEnumerable <łańcuch> z samym komunikatem o błędzie :: var allErrors = ModelState.Values.SelectMany (v => v.Errors.Select (b => b.ErrorMessage));
Kieran
6
To jest świetne, ale niestety okna Watch / Immediate nie obsługują lambda :(
AaronLS
3
Tak! Ja (ty, ktokolwiek) potrzebuje „korzystania z System.Linq;” na szczycie. W przeciwnym razie pojawi się komunikat „Wartości nie zawierają definicji Wybierz wiele”. W moim przypadku brakowało.
Estevez,
2
dlaczego do diabła używa var ?????? nie mógłbyś zamiast tego napisać „IEnumerable <ModelError>”?
Hakan Fıstık
6
@ hakam-fostok @ jb06 masz rację. Pisanie List<string> errors = new List<string>()zamiast var errors = new List<string>()jest naprawdę stratą czasu, ale pisanie IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);, w którym typ zwracany nie jest tak naprawdę klarowny, jest naprawdę większe pod względem czytelności. (nawet jeśli studio wizualne może dać ci to po najechaniu myszą)
aprovent
192

Opierając się na wersji LINQ, jeśli chcesz połączyć wszystkie komunikaty o błędach w jeden ciąg:

string messages = string.Join("; ", ModelState.Values
                                        .SelectMany(x => x.Errors)
                                        .Select(x => x.ErrorMessage));
Dunc
źródło
5
Inną opcją jest wykonanie następujących czynności: ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .JoinString (";");
Tod Thomson,
3
@Tod, czy IEnumerable.JoinString () jest Twoją własną metodą rozszerzenia? Zobacz stackoverflow.com/q/4382034/188926
Dunc
2
Hej Dunc - tak, podejrzewam, że dodałem tę metodę rozszerzenia do mojej bazy kodu i zapomniałem o niej, a potem pomyślałem, że to metoda ramowa LOL :(
Tod Thomson
5
lub ... ModelState.Values.SelectMany (O => O.Errors) .Select (O => O.ErrorMessage) .Aggregate ((U, V) => U + "," + V)
fordareh
2
Działa to świetnie, gdy używasz interfejsu WWW i zwracasz wynik IHttpActionResult. Możesz po prostu zrobić: zwróć BadRequest (wiadomości); Dzięki, Dunc!
Rich Ward
32

Byłem w stanie to zrobić za pomocą małego 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;
}

Powyższa metoda zwraca listę błędów sprawdzania poprawności.

Dalsza lektura:

Jak odczytać wszystkie błędy z ModelState w ASP.NET MVC

Yasser Shaikh
źródło
17

Podczas debugowania przydatne jest umieszczenie tabeli na dole każdej strony, aby wyświetlić wszystkie błędy 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
źródło
jeśli są tutaj jakieś przypadki krawędzi, w których to się nie udaje, po prostu edytuj odpowiedź, aby to naprawić
Simon_Weaver,
12

Jak odkryłem, postępując zgodnie z radą zawartą w odpowiedziach udzielonych do tej pory, możesz uzyskać wyjątki występujące bez ustawiania komunikatów o błędach, więc aby złapać wszystkie problemy, naprawdę potrzebujesz zarówno komunikatu o błędzie, jak i wyjątku.

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

lub jako metoda rozszerzenia

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

}
Alan Macdonald
źródło
dlaczego chcesz ciąg z wszystkimi błędami? nie ma sensu, gdy chcesz coś z tym zrobić w widoku, tablica list jest o wiele lepsza imho
Daniël Tulp
1
Aby debugować. Moim pierwszym problemem było ustalenie, co poszło nie tak z moją aplikacją. Nie próbowałem powiedzieć użytkownikowi, żeby dowiedział się, co poszło nie tak. Poza tym nie jest łatwo przekonwertować ten przykład z tworzenia wyliczenia ciągów na wyliczenie czegoś innego, np. Komunikatu o błędzie i wyjątku, więc naprawdę przydatną rzeczą jest wiedza, że ​​potrzebne są oba fragmenty informacji
Alan Macdonald
BTW, zdałeś sobie sprawę, że druga metoda rozszerzenia zwraca IEnumerable <String>, a nie tylko duży pojedynczy ciąg?
Alan Macdonald
8

W przypadku, gdy ktoś chce zwrócić właściwość Nazwa modelu za powiązanie komunikatu o błędzie w silnie typowanym widoku.

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

W ten sposób możesz powiązać błąd z polem, które go zgłosiło.

james31rock
źródło
7

Samo wysyłanie samych komunikatów o błędach nie było dla mnie wystarczające, ale to załatwiło sprawę.

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
źródło
1
Jako ostrzeżenie pary kluczowych wartości w ModelState mogą zawierać wartości NULL, dlatego oryginalny kod tutaj zawierał trochę uroczych firm C # 6 z operatorem zerowej koalescencji (?.), Stąd curry do ?? na końcu wyrażenia. Pierwotne wyrażenie, które powinno chronić przed błędami zerowymi, brzmiało: state.Value.?AttemptedValue ?? "[ZERO]". O ile mi wiadomo, kod w jego obecnym stanie, bez podstępnej obsługi przypadków, w których state.Value == null, jest zagrożony.
Josh Sutterfield
5

Na wypadek, gdyby ktoś tego potrzebował, stworzyłem i stosuję następującą klasę statyczną w moich projektach

Przykład użycia:

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

Zastosowania:

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

Klasa:

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
źródło
Dzięki CodeArtist !! Wprowadziłem niewielką zmianę w kodzie poniżej jego implementacji.
Alfred Severo
4

I to też działa:

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
źródło
@Yasser Czy widziałeś odpowiedź Toto?
The Muffin Man
@TheMuffinMan tak mam. Co z tym ?
Yasser Shaikh
@Yasser To najlepsza odpowiedź. Nie ma w tym nic złego, ale nie ma sensu używać go, gdy SelectManyjest dostępny.
The Muffin Man
4

Przydatne do przekazywania tablicy komunikatów o błędach do View, być może przez Json:

messageArray = this.ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors, (modelState, error) => error.ErrorMessage).ToArray();
Steve Lydford
źródło
4

To rozwija się w odpowiedzi na odpowiedź @Dunc. Zobacz komentarze do dokumentu 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
źródło
3

Ponadto ModelState.Values.ErrorMessagemoże być pusty, ale ModelState.Values.Exception.Messagemoże wskazywać na błąd.

Jason Dufair
źródło
0

W twojej implementacji brakuje klasy statycznej, tak powinno być.

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

raczej

if (!ModelState.IsValid)
{
    var errors = ModelState.GetModelErrors();
    return Json(new { errors });
}
Alfred Severo
źródło
0

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

po prostu użyj Tag Helper Tag-podsumowanie-podsumowanie

Armin Azhdari
źródło