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?
fuente
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?
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? .
Value.ErrorMessage
y, en su lugar, unValue.Exception.Message
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
Usando LINQ :
IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);
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)
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));
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 :
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>
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();
}
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.
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));
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();
}
}
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...
SelectMany
está disponible.
Ú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();
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;
}
}
Además, ModelState.Values.ErrorMessage
puede estar vacío, pero ModelState.Values.Exception.Message
puede indicar un error.
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 });
}
<div class="text-danger" style="direction:rtl" asp-validation-summary="All"></div>
simplemente use Asistente de etiqueta asp-validation-summary
@Html.ValidationSummary()
es una forma rápida de mostrarlos a todos en maquinilla de afeitar.foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }