Come ottenere tutti gli errori da ASP.Net MVC modelState?


453

Voglio ottenere tutti i messaggi di errore da modelState senza conoscere i valori chiave. Scorrimento continuo per afferrare tutti i messaggi di errore contenuti in ModelState.

Come posso fare questo?


5
Se stai solo visualizzando gli errori, allora @Html.ValidationSummary()è un modo rapido per visualizzarli tutti a rasoio.
levininja,

11
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }
Razvan Dumitru,

Risposte:


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

Vedi anche Come posso ottenere la raccolta di errori di stato del modello in ASP.NET MVC? .


22
Molto utile. Nota in alcuni scenari, come errori di associazione e richieste errate, ci saranno voci ModelState con stringa vuota per Value.ErrorMessagee invece unValue.Exception.Message
AaronLS

5
Gli errori sono buoni, ma a volte vuoi anche la chiave del modelstate (ovvero il nome del campo). è possibile ottenere che cambiando la prima linea a questo: foreach (KeyValuePair<string, ModelState> kvp in htmlHelper.ViewData.ModelState) {e inserire questa riga sotto di essa: var modelState = kvp.Value;. È possibile ottenere la chiave dakvp.Key
vigore

534

Utilizzando LINQ :

IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);

69
Modificato per restituire IEnumerable <string> con solo il messaggio di errore :: var allErrors = ModelState.Values.SelectMany (v => v.Errors.Select (b => b.ErrorMessage));
Kieran,

6
Questo è fantastico, ma purtroppo le finestre Watch / Immediate non supportano le lambda :(
AaronLS

3
Sì! Io (tu, chiunque) ho bisogno di "usare System.Linq;" In cima. Altrimenti viene visualizzato il messaggio "I valori non contengono una definizione per Seleziona molti". Nel mio caso mancava.
Estevez,

2
perché diavolo usando di var ?????? non potresti scrivere `IEnumerable <ModelError> 'invece ???
Hakan Fıstık,

6
@ hakam-fostok @ jb06 hai ragione entrambi. Digitare List<string> errors = new List<string>()invece var errors = new List<string>()è davvero una perdita di tempo, ma la scrittura IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);, in cui il tipo di ritorno non è veramente chiaro, è davvero maggiore in termini di leggibilità. (anche se Visual Studio può
dartelo al

192

Basandosi sulla versione LINQ, se si desidera unire tutti i messaggi di errore in una stringa:

string messages = string.Join("; ", ModelState.Values
                                        .SelectMany(x => x.Errors)
                                        .Select(x => x.ErrorMessage));

5
L'altra opzione è fare quanto segue: ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .JoinString (";");
Tod Thomson,

3
@Tod, IEnumerable.JoinString () è il tuo metodo di estensione? Vedi stackoverflow.com/q/4382034/188926
Dunc il

2
Ehi Dunc - sì, sospetto di aver aggiunto quel metodo di estensione alla mia base di codice e me ne sono dimenticato e poi ho pensato che fosse un metodo framework LOL :(
Tod Thomson,

5
oppure ... ModelState.Values.SelectMany (O => O.Errors) .Seleziona (O => O.ErrorMessage) .Aggregate ((U, V) => U + "," + V)
fordareh

2
Funziona perfettamente quando si utilizza l'API Web e si restituisce un risultato IHttpActionResult. Quindi, puoi semplicemente fare: return BadRequest (messaggi); Grazie Dunc!
Rich Ward,

32

Sono stato in grado di farlo usando un po 'di 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;
}

Il metodo sopra riportato restituisce un elenco di errori di convalida.

Ulteriori letture:

Come leggere tutti gli errori di ModelState in ASP.NET MVC


17

Durante il debug trovo utile mettere una tabella in fondo a ciascuna delle mie pagine per mostrare tutti gli errori 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>

se ci sono casi limite qui in cui ciò fallisce, basta modificare la risposta per risolverlo
Simon_Weaver

12

Come ho scoperto di aver seguito il consiglio nelle risposte fornite finora, è possibile che si verifichino eccezioni senza che vengano impostati messaggi di errore, quindi per cogliere tutti i problemi necessari per ottenere sia ErrorMessage sia l'eccezione.

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

o come metodo di estensione

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

}

perché vorresti una stringa con tutti gli errori in essa contenuti? non ha senso quando vuoi fare qualcosa con esso nella vista, una serie di elenchi è molto meglio imho
Daniël Tulp

1
Per eseguire il debug. Il mio primo problema era scoprire cosa non andava nella mia app. Non stavo cercando di dire all'utente di scoprire cosa stava andando storto. Inoltre è banale convertire quell'esempio dalla creazione di un elenco di stringhe in un elenco di qualcos'altro, ad esempio un messaggio di errore e un'eccezione, quindi la cosa davvero utile è sapere che hai bisogno di entrambi i bit di informazione
Alan Macdonald,

A proposito, hai realizzato che il secondo metodo di estensione restituisce IEnumerable <String> e non solo una grande stringa singola?
Alan Macdonald,

8

Nel caso in cui qualcuno desideri restituire la proprietà Nome del modello per associare il messaggio di errore in una vista fortemente tipizzata.

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

In questo modo puoi effettivamente collegare l'errore con il campo che ha generato l'errore.


7

Emettere solo i messaggi di errore non era sufficiente per me, ma questo ha funzionato.

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
Come avvertimento, le coppie di valori-chiave in ModelState possono includere valori NULL, motivo per cui il codice originale qui includeva alcune attività C # 6 carine con un operatore null-coalesce (?.), Quindi il curry al ?? alla fine dell'espressione. L'espressione originale che dovrebbe proteggere da errori null era: state.Value.?AttemptedValue ?? "[NULLO]". Per quanto ne so, il codice nel suo stato attuale, senza la subdola gestione dei casi in cui state.Value == null, è a rischio.
Josh Sutterfield,

5

Nel caso in cui qualcuno ne avesse bisogno, ho creato e usato la seguente classe statica nei miei progetti

Esempio di utilizzo:

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

usings:

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

Grazie CodeArtist !! Ho apportato una piccola modifica al codice sotto la sua implementazione.
Alfred Severo,

4

E funziona anche questo:

var query = from state in ModelState.Values
    from error in state.Errors
    select error.ErrorMessage;
var errors = query.ToArray(); // ToList() and so on...

@Yasser Hai visto la risposta di Toto?
The Muffin Man

@TheMuffinMan sì, l'ho fatto. Che ne dici?
Yasser Shaikh,

@Yasser È la risposta migliore. Niente di sbagliato in questo, ma inutile usarlo quando SelectManyè disponibile.
The Muffin Man

4

Utile per passare array di messaggi di errore da visualizzare, magari tramite Json:

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

4

Questo si sta espandendo sulla risposta di @Dunc. Vedi commenti 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;            
    }
}

3

Inoltre, ModelState.Values.ErrorMessagepuò essere vuoto, ma ModelState.Values.Exception.Messagepuò indicare un errore.


0

Nella tua implementazione ti manca la classe statica, questo dovrebbe essere.

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

piuttosto

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

0

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

usa semplicemente Tag Helper di riepilogo di validazione asp

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.