¿Cómo obtener todos los errores de ASP.Net MVC modelState?

453

Quiero obtener todos los mensajes de error del modelState sin conocer los valores clave. Recorriendo para obtener todos los mensajes de error que contiene ModelState.

¿Cómo puedo hacer esto?

chobo2
fuente
55
Si solo está mostrando los errores, entonces @Html.ValidationSummary()es una forma rápida de mostrarlos a todos en maquinilla de afeitar.
levininja
11
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }
Razvan Dumitru

Respuestas:

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

Consulte también ¿Cómo obtengo la colección de errores de estado del modelo en ASP.NET MVC? .

Oren Trutner
fuente
22
Muy útil. Tenga en cuenta que en algunos escenarios, como fallas de enlace y solicitudes incorrectas, habrá entradas de ModelState con una cadena vacía Value.ErrorMessagey, en su lugar, unValue.Exception.Message
AaronLS
55
Los errores son agradables, pero a veces también desea la clave del estado del modelo (es decir, el nombre del campo). usted puede conseguir que al cambiar la primera línea a la siguiente: foreach (KeyValuePair<string, ModelState> kvp in htmlHelper.ViewData.ModelState) {e insertar esta línea debajo de él: var modelState = kvp.Value;. Puede obtener la clave dekvp.Key
viggity
534

Usando LINQ :

IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);
mmutilva
fuente
69
Modificado para devolver IEnumerable <cadena> con solo el mensaje de error :: var allErrors = ModelState.Values.SelectMany (v => v.Errors.Select (b => b.ErrorMessage));
Kieran
66
Esto es genial, pero desafortunadamente las ventanas Watch / Inmediate no son compatibles con lambda :(
AaronLS
3
¡Si! Yo (usted, cualquiera) necesita "usar System.Linq;" En lo alto. De lo contrario, aparece el mensaje 'Los valores no contienen una definición para Seleccionar muchos'. Faltaba en mi caso.
Estévez
2
¿Por qué demonios usando var ?????? ¿No podrías escribir `IEnumerable <ModelError> 'en su lugar?
Hakan Fıstık
66
@ hakam-fostok @ jb06 ambos tienen razón. Escribir en List<string> errors = new List<string>()lugar de var errors = new List<string>()es realmente una pérdida de tiempo, pero escribir IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);, donde el tipo de retorno no es realmente claro, es realmente mayor en términos de legibilidad. (incluso si Visual Studio puede dárselo con el mouse)
aprovent
192

Basándose en la versión LINQ, si desea unir todos los mensajes de error en una sola cadena:

string messages = string.Join("; ", ModelState.Values
                                        .SelectMany(x => x.Errors)
                                        .Select(x => x.ErrorMessage));
Dunc
fuente
55
La otra opción es hacer lo siguiente: ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .JoinString (";");
Tod Thomson
3
@Tod, ¿es IEnumerable.JoinString () tu propio método de extensión? Ver stackoverflow.com/q/4382034/188926
Dunc
2
Hola Dunc: sí, sospecho que agregué ese método de extensión a mi base de código y me olvidé de él y luego pensé que era un método marco LOL :(
Tod Thomson
55
o ... ModelState.Values.SelectMany (O => O.Errors) .Seleccione (O => O.ErrorMessage) .Aggregate ((U, V) => U + "," + V)
fordareh
2
Esto funciona muy bien cuando usa la API web y devuelve un resultado IHttpActionResult. Por lo tanto, puede hacer lo siguiente: devolver BadRequest (mensajes); Gracias Dunc!
Rich Ward el
32

Pude hacer esto usando un pequeño 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;
}

El método anterior devuelve una lista de errores de validación.

Otras lecturas :

Cómo leer todos los errores de ModelState en ASP.NET MVC

Yasser Shaikh
fuente
17

Durante la depuración me resulta útil poner una tabla en la parte inferior de cada una de mis páginas para mostrar todos los errores de 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
fuente
si hay casos
extremos
12

Como descubrí que he seguido los consejos en las respuestas dadas hasta ahora, puede obtener excepciones sin que se configuren mensajes de error, por lo que para detectar todos los problemas, realmente necesita obtener tanto el Mensaje de error como la Excepción.

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

o como un método de extensión

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

}
Alan Macdonald
fuente
¿Por qué quieres una cadena con todos los errores? no tiene sentido cuando quieres hacer algo con él en la vista, una matriz de lista es mucho mejor en mi humilde opinión
Daniël Tulp
1
Depurar. Mi primer problema fue averiguar qué iba mal con mi aplicación. No estaba tratando de decirle al usuario que solo descubriera qué estaba mal. Además, es trivial convertir ese ejemplo al crear una enumeración de cadenas en una enumeración de otra cosa, por ejemplo, mensaje de error y excepción, por lo que lo realmente útil es saber que necesita ambos bits de información
Alan Macdonald
Por cierto, ¿se dio cuenta de que el segundo método de extensión devuelve IEnumerable <String> y no solo una gran cadena única?
Alan Macdonald
8

En caso de que alguien quiera devolver la propiedad Nombre del modelo para vincular el mensaje de error en una vista fuertemente 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);
    }
}

De esta forma, puede vincular el error con el campo que arrojó el error.

james31rock
fuente
7

Enviar solo los mensajes de error en sí no fue suficiente para mí, pero esto funcionó.

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
fuente
1
Como advertencia, los pares de valores clave en ModelState pueden incluir valores NULL, razón por la cual el código original aquí incluía algunos negocios lindos de C # 6 con un operador de fusión nula (?.), De ahí el curry al ?? Al final de la expresión. La expresión original que debería proteger de los errores nulos fue: state.Value.?AttemptedValue ?? "[NULO]". Hasta donde yo sé, el código en su estado actual, sin el manejo furtivo de casos en los que state.Value == null, está en riesgo.
Josh Sutterfield
5

Por si acaso alguien lo necesita, lo hice y uso la siguiente clase estática en mis proyectos

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

Clase:

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
fuente
Gracias CodeArtist !! Hice un pequeño cambio en el código debajo de su implementación.
Alfred Severo
4

Y esto también funciona:

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
fuente
@Yasser ¿Has visto la respuesta de Toto?
The Muffin Man
@TheMuffinMan sí, tengo. ¿Qué hay de eso?
Yasser Shaikh
@Yasser Es la mejor respuesta. No hay nada malo con este, pero no tiene sentido usarlo cuando SelectManyestá disponible.
The Muffin Man
4

Útil para pasar una matriz de mensajes de error a View, quizás a través de Json:

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

Esto se está ampliando con la respuesta de @Dunc. Ver comentarios 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
fuente
3

Además, ModelState.Values.ErrorMessagepuede estar vacío, pero ModelState.Values.Exception.Messagepuede indicar un error.

Jason Dufair
fuente
0

En su implementación, le falta Clase estática, esto debería ser.

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

más bien

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

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

simplemente use Asistente de etiqueta asp-validation-summary

Armin Azhdari
fuente