Como obter todos os erros do ASP.Net MVC modelState?

453

Desejo obter todas as mensagens de erro do modelState sem conhecer os valores-chave. Fazendo um loop para pegar todas as mensagens de erro que o ModelState contém.

Como posso fazer isso?

chobo2
fonte
5
Se você está apenas exibindo os erros, @Html.ValidationSummary()é uma maneira rápida de exibi-los todos com navalha.
precisa saber é o seguinte
11
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }
precisa

Respostas:

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

Consulte também Como obtenho a coleção de erros de estado do modelo no ASP.NET MVC? .

Oren Trutner
fonte
22
Muito útil. Observe que em alguns cenários, como falhas de ligação e solicitações incorretas, haverá entradas ModelState com uma sequência vazia para Value.ErrorMessageeValue.Exception.Message
AaronLS
5
Os erros são bons, mas às vezes você também deseja a chave do estado do modelo (ou seja, o nome do campo). você pode começar que, alterando a primeira linha para isso: foreach (KeyValuePair<string, ModelState> kvp in htmlHelper.ViewData.ModelState) {e inserir esta linha abaixo dela: var modelState = kvp.Value;. Você pode obter a chavekvp.Key
viggity
534

Usando LINQ :

IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);
mmutilva
fonte
69
Modificado para retornar IEnumerable <string> com apenas a mensagem de erro :: var allErrors = ModelState.Values.SelectMany (v => v.Errors.Select (b => b.ErrorMessage));
Kieran
6
Isso é ótimo, mas, infelizmente, as janelas Watch / Immediate não oferecem suporte a lambda's :(
AaronLS 31/03
3
Sim! Eu (você, qualquer pessoa) precisa "using System.Linq;" no topo. Caso contrário, você receberá a mensagem 'Valores não contém uma definição para Selecionar muitos'. Estava faltando no meu caso.
Estevez
2
por que diabos usando var var ?????? você não poderia escrever `IEnumerable <ModelError> 'em vez disso ???
Hakan Fıstık
6
@ hakam-fostok @ jb06 vocês dois estão certos. Digitar em List<string> errors = new List<string>()vez de var errors = new List<string>()é realmente uma perda de tempo, mas escrever IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);, onde o tipo de retorno não é realmente claro, é realmente maior em termos de legibilidade. (mesmo estúdio visual pode dar a você no foco do mouse)
aprovent
192

Com base na versão LINQ, se você deseja unir todas as mensagens de erro em uma sequência:

string messages = string.Join("; ", ModelState.Values
                                        .SelectMany(x => x.Errors)
                                        .Select(x => x.ErrorMessage));
Dunc
fonte
5
A outra opção é fazer o seguinte: ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .JoinString (";");
Tod Thomson
3
@Tod, IEnumerable.JoinString () é seu próprio método de extensão? Veja stackoverflow.com/q/4382034/188926
Dunc
2
Ei Dunc - sim, eu suspeito que adicionei esse método de extensão à minha base de código e esqueci-o e pensei que era um método de estrutura LOL :(
Tod Thomson
5
ou ... ModelState.Values.SelectMany (O => O.Errors). Selecione (O => O.ErrorMessage) .Aggregate ((U, V) => U + "," + V)
fordareh
2
Isso funciona muito bem quando você usa a API da Web e retorna um resultado IHttpActionResult. Então, você pode simplesmente fazer: return BadRequest (messages); Obrigado, Dunc!
Rich Ward
32

Consegui fazer isso usando um pouco de 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;
}

O método acima retorna uma lista de erros de validação.

Leitura adicional:

Como ler todos os erros do ModelState no ASP.NET MVC

Yasser Shaikh
fonte
17

Durante a depuração, acho útil colocar uma tabela na parte inferior de cada uma das minhas páginas para mostrar todos os erros do 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
fonte
se há quaisquer casos de ponta aqui onde esta falha agradar apenas editar a resposta para corrigi-lo
Simon_Weaver
12

Como descobri que segui o conselho das respostas fornecidas até agora, é possível obter exceções sem que as mensagens de erro sejam definidas. Para detectar todos os problemas, você realmente precisa obter o ErrorMessage e o Exception.

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

ou como um método de extensão

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

}
Alan Macdonald
fonte
por que você quer uma string com todos os erros nela? não faz sentido quando você quer fazer algo com ele na vista, uma série de lista é muito melhor imho
Daniël Tulp
1
Para depurar. Meu primeiro problema foi descobrir o que estava acontecendo de errado com meu aplicativo. Eu não estava tentando dizer ao usuário apenas para descobrir o que estava errado. Além disso, é trivial para converter esse exemplo da criação de uma enumeração de cordas para uma enumeração de qualquer outra coisa, por exemplo, mensagem de erro e de exceção assim que a coisa realmente útil é saber que você precisa de ambos os bits de informação
Alan Macdonald
BTW, você percebeu que o segundo método de extensão retorna IEnumerable <> e não apenas uma grande string?
22413 Alan Macdonald
8

Caso alguém queira retornar a propriedade Name of the Model para vincular a mensagem de erro em uma exibição fortemente tipada.

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

Dessa forma, você pode realmente vincular o erro ao campo que o lançou.

james31rock
fonte
7

Enviar apenas as mensagens de erro não foi suficiente para mim, mas foi o que fez.

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
fonte
1
Como um aviso, os pares de valores-chave no ModelState podem incluir valores NULL, e é por isso que o código original aqui incluiu alguns negócios C # 6 bonitos com um operador de coalescência nula (?.), Portanto, o valor para o ?? no final da expressão. A expressão original que deveria proteger contra erros nulos era: state.Value.?AttemptedValue ?? "[NULO]". Tanto quanto eu sei, o código em seu estado atual, sem o tratamento furtivo de casos em que state.Value == null, está em risco.
Josh Sutterfield
5

Para o caso de alguém precisar, eu fiz e usei a seguinte classe estática em meus projetos

Exemplo de uso:

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

Usos:

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
fonte
Obrigado CodeArtist !! Fiz uma pequena alteração no código abaixo de sua implementação.
Alfred Severo
4

E isso também funciona:

var query = from state in ModelState.Values
    from error in state.Errors
    select error.ErrorMessage;
var errors = query.ToArray(); // ToList() and so on...
ruivo amiry
fonte
@Yasser Você já viu a resposta de Toto?
The Muffin Man
@TheMuffinMan sim eu tenho. Que tal isso?
Yasser Shaikh
@Yasser É a melhor resposta. Nada de errado com este, mas não adianta usá-lo quando SelectManyestiver disponível.
O homem de muffin
4

Útil para passar uma série de mensagens de erro para o View, talvez via Json:

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

Isso está se expandindo com a resposta de @Dunc. Ver comentários de documentos 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
fonte
3

Além disso, ModelState.Values.ErrorMessagepode estar vazio, mas ModelState.Values.Exception.Messagepode indicar um erro.

Jason Dufair
fonte
0

Na sua implementação, está faltando uma classe estática, deve ser.

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

em vez

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

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

simplesmente use asp-validation-summary Tag Helper

Armin Azhdari
fonte