c# - ASP.NET MVC Cómo convertir errores de ModelState a json
asp.net-mvc linq (11)
¿Cómo se obtiene una lista de todos los mensajes de error de ModelState? Encontré este código para obtener todas las claves: ( Devolviendo una lista de claves con errores de ModelState )
var errorKeys = (from item in ModelState
where item.Value.Errors.Any()
select item.Key).ToList();
Pero, ¿cómo obtendría los mensajes de error como IList o IQueryable?
Podría ir:
foreach (var key in errorKeys)
{
string msg = ModelState[error].Errors[0].ErrorMessage;
errorList.Add(msg);
}
Pero eso es hacerlo manualmente. ¿Hay una forma de hacerlo con LINQ? La propiedad .ErrorMessage está tan abajo en la cadena que no sé cómo escribir el LINQ ...
¿Por qué no devuelve el objeto ModelState
original al cliente y luego utiliza jQuery para leer los valores? Para mí, parece mucho más simple y usa la estructura de datos común ( ModelState
de .net)
para devolver ModelState
como Json, simplemente páselo al constructor de la clase Json (funciona con CUALQUIER objeto)
DO#:
return Json(ModelState);
js:
var message = "";
if (e.response.length > 0) {
$.each(e.response, function(i, fieldItem) {
$.each(fieldItem.Value.Errors, function(j, errItem) {
message += errItem.ErrorMessage;
});
message += "/n";
});
alert(message);
}
@JK me ayudó mucho, pero ¿por qué no?
public class ErrorDetail {
public string fieldName = "";
public string[] messageList = null;
}
if (!modelState.IsValid)
{
var errorListAux = (from m in modelState
where m.Value.Errors.Count() > 0
select
new ErrorDetail
{
fieldName = m.Key,
errorList = (from msg in m.Value.Errors
select msg.ErrorMessage).ToArray()
})
.AsEnumerable()
.ToDictionary(v => v.fieldName, v => v);
return errorListAux;
}
Aquí está la implementación completa con todas las piezas juntas:
Primero crea un método de extensión:
public static class ModelStateHelper
{
public static IEnumerable Errors(this ModelStateDictionary modelState)
{
if (!modelState.IsValid)
{
return modelState.ToDictionary(kvp => kvp.Key,
kvp => kvp.Value.Errors
.Select(e => e.ErrorMessage).ToArray())
.Where(m => m.Value.Count() > 0);
}
return null;
}
}
Luego llame a ese método de extensión y devuelva los errores de la acción del controlador (si los hubiera) como json:
if (!ModelState.IsValid)
{
return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet);
}
Y finalmente, muestre esos errores en el lado del cliente (en estilo jquery.validation, pero se puede cambiar fácilmente a cualquier otro estilo)
function DisplayErrors(errors) {
for (var i = 0; i < errors.length; i++) {
$("<label for=''" + errors[i].Key + "'' class=''error''></label>")
.html(errors[i].Value[0]).appendTo($("input#" + errors[i].Key).parent());
}
}
Eche un vistazo a System.Web.Http.Results.OkNegotiatedContentResult.
Convierte todo lo que le arrojas a JSON.
Así que hice esto
var errorList = ModelState.ToDictionary(kvp => kvp.Key.Replace("model.", ""), kvp => kvp.Value.Errors[0].ErrorMessage);
return Ok(errorList);
Esto resultó en:
{
"Email":"The Email field is not a valid e-mail address."
}
Todavía tengo que verificar qué sucede cuando hay más de un error para cada campo, pero el punto es que OkNegoriatedContentResult es brillante.
Obtuve la idea linq / lambda de @SLaks
Hay muchas formas diferentes de hacer esto que funcionan. Aquí es ahora lo hago ...
if (ModelState.IsValid)
{
return Json("Success");
}
else
{
return Json(ModelState.Values.SelectMany(x => x.Errors));
}
La forma más sencilla de hacerlo es simplemente devolver un BadRequest
con el mismo ModelState:
Por ejemplo en un PUT
:
[HttpPut]
public async Task<IHttpActionResult> UpdateAsync(Update update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// perform the update
return StatusCode(HttpStatusCode.NoContent);
}
Si utilizamos anotaciones de datos en, por ejemplo, un número de móvil, como este, en la clase de Update
:
public class Update {
[StringLength(22, MinimumLength = 8)]
[RegularExpression(@"^/d{8}$|^00/d{6,20}$|^/+/d{6,20}$")]
public string MobileNumber { get; set; }
}
Esto devolverá lo siguiente en una solicitud no válida:
{
"Message": "The request is invalid.",
"ModelState": {
"update.MobileNumber": [
"The field MobileNumber must match the regular expression ''^//d{8}$|^00//d{6,20}$|^//+//d{6,20}$''.",
"The field MobileNumber must be a string with a minimum length of 8 and a maximum length of 22."
]
}
}
Me gusta usar Hashtable
aquí, para obtener el objeto JSON con propiedades como claves y errores como valor en forma de matriz de cadenas.
var errors = new Hashtable();
foreach (var pair in ModelState)
{
if (pair.Value.Errors.Count > 0)
{
errors[pair.Key] = pair.Value.Errors.Select(error => error.ErrorMessage).ToList();
}
}
return Json(new { success = false, errors });
De esta forma obtienes la siguiente respuesta:
{
"success":false,
"errors":{
"Phone":[
"The Phone field is required."
]
}
}
Puedes poner lo que quieras dentro de la cláusula de select
:
var errorList = (from item in ModelState
where item.Value.Errors.Any()
select item.Value.Errors[0].ErrorMessage).ToList();
EDITAR : Puede extraer múltiples errores en elementos de lista separados agregando una cláusula from
, como esta:
var errorList = (from item in ModelState.Values
from error in item.Errors
select error.ErrorMessage).ToList();
O:
var errorList = ModelState.Values.SelectMany(m => m.Errors)
.Select(e => e.ErrorMessage)
.ToList();
2da EDIT : Estás buscando un Dictionary<string, string[]>
:
var errorList = ModelState.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray()
);
ToDictionary es una extensión enumerable que se encuentra en System.Linq empaquetado en System.Web.Extensions dll http://msdn.microsoft.com/en-us/library/system.linq.enumerable.todictionary.aspx . Esto es lo que la clase completa parece para mí.
using System.Collections;
using System.Web.Mvc;
using System.Linq;
namespace MyNamespace
{
public static class ModelStateExtensions
{
public static IEnumerable Errors(this ModelStateDictionary modelState)
{
if (!modelState.IsValid)
{
return modelState.ToDictionary(kvp => kvp.Key,
kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray()).Where(m => m.Value.Count() > 0);
}
return null;
}
}
}
Variación con tipo de retorno en lugar de devolver IEnumerable
public static class ModelStateHelper
{
public static IEnumerable<KeyValuePair<string, string[]>> Errors(this ModelStateDictionary modelState)
{
if (!modelState.IsValid)
{
return modelState
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray())
.Where(m => m.Value.Any());
}
return null;
}
}
List<ErrorList> Errors = new List<ErrorList>();
//test errors.
var modelStateErrors = this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors);
foreach (var x in modelStateErrors)
{
var errorInfo = new ErrorList()
{
ErrorMessage = x.ErrorMessage
};
Errors.Add(errorInfo);
}
si usas jsonresult entonces regresa
return Json(Errors);
o simplemente puede devolver el modelStateErrors, no lo he intentado. Lo que hice fue asignar la colección Errors a mi ViewModel y luego ponerle un bucle. En este caso, puedo devolver mis Errors a través de json. Tengo una clase / modelo, quería obtener la fuente / clave, pero aún estoy tratando de resolverlo.
public class ErrorList
{
public string ErrorMessage;
}