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ć?


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? .


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

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

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


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>

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

}

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.


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

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

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...

@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();

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

3

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


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

0

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

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.