ASP.NET MVC Как конвертировать ошибки ModelState в json - PullRequest
121 голосов
/ 17 мая 2010

Как получить список всех сообщений об ошибках ModelState? Я нашел этот код, чтобы получить все ключи: ( Возвращение списка ключей с ошибками ModelState )

var errorKeys = (from item in ModelState
        where item.Value.Errors.Any() 
        select item.Key).ToList();

Но как мне получить сообщения об ошибках в виде IList или IQueryable?

Я мог бы пойти:

foreach (var key in errorKeys)
{
    string msg = ModelState[error].Errors[0].ErrorMessage;
    errorList.Add(msg);
}

Но это делается вручную - наверняка есть способ сделать это с помощью LINQ? Свойство .ErrorMessage находится так далеко вниз по цепочке, что я не знаю, как написать LINQ ...

Ответы [ 13 ]

180 голосов
/ 17 мая 2010

Вы можете поместить что угодно , которое вы хотите, внутри предложения select:

var errorList = (from item in ModelState
        where item.Value.Errors.Any() 
        select item.Value.Errors[0].ErrorMessage).ToList();

РЕДАКТИРОВАНИЕ : Вы можете извлечь несколько ошибок в отдельные элементы списка, добавив предложение from, например:

var errorList = (from item in ModelState.Values
        from error in item.Errors
        select error.ErrorMessage).ToList();

Или:

var errorList = ModelState.Values.SelectMany(m => m.Errors)
                                 .Select(e => e.ErrorMessage)
                                 .ToList();

2 и РЕДАКТИРОВАТЬ : Вы ищете Dictionary<string, string[]>:

var errorList = ModelState.ToDictionary(
    kvp => kvp.Key,
    kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray()
);
73 голосов
/ 17 мая 2010

Вот полная реализация со всеми кусками вместе взятыми:

Сначала создайте метод расширения:

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.Any());
        }
        return null;
    }
}

Затем вызовите этот метод расширения и верните ошибки из действия контроллера (если есть) как json:

if (!ModelState.IsValid)
{
    return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet);
}

И, наконец, показать эти ошибки на стороне клиента (в стиле jquery.validation, но его можно легко изменить на любой другой стиль)

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());
    }
}
22 голосов
/ 05 сентября 2013

Мне нравится использовать Hashtable здесь, так что я получаю объект JSON со свойствами в качестве ключей и ошибки в качестве значения в виде массива строк.

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

Таким образом, вы получите следующий ответ:

{
   "success":false,
   "errors":{
      "Phone":[
         "The Phone field is required."
      ]
   }
}
7 голосов
/ 29 ноября 2013

Есть много разных способов сделать это, которые все работают. Вот сейчас я это делаю ...

if (ModelState.IsValid)
{
    return Json("Success");
}
else
{
    return Json(ModelState.Values.SelectMany(x => x.Errors));
}
5 голосов
/ 25 ноября 2012

@ JK это мне очень помогло, но почему бы и нет:

 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;
        }
3 голосов
/ 24 ноября 2015

Самый простой способ сделать это - просто вернуть BadRequest с самим ModelState:

Например, на PUT:

[HttpPut]
public async Task<IHttpActionResult> UpdateAsync(Update update)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    // perform the update

    return StatusCode(HttpStatusCode.NoContent);
}

Если мы используем аннотации данных, например, такой номер мобильного телефона в классе Update:

public class Update {
    [StringLength(22, MinimumLength = 8)]
    [RegularExpression(@"^\d{8}$|^00\d{6,20}$|^\+\d{6,20}$")]
    public string MobileNumber { get; set; }
}

При неправильном запросе будет возвращено следующее:

{
  "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."
    ]
  }
}
3 голосов
/ 14 октября 2015

Взгляните на System.Web.Http.Results.OkNegotiatedContentResult.

Он преобразует все, что вы бросаете в него, в JSON.

Итак, я сделал это

var errorList = ModelState.ToDictionary(kvp => kvp.Key.Replace("model.", ""), kvp => kvp.Value.Errors[0].ErrorMessage);

return Ok(errorList);

Это привело к:

{
  "Email":"The Email field is not a valid e-mail address."
}

Мне еще предстоит проверить, что происходит, когда существует более одной ошибки для каждого поля, но суть в том, что OkNegoriatedContentResult просто великолепен!

Получил идею linq / lambda от @ SLaks

2 голосов
/ 15 августа 2018

Простой способ добиться этого с помощью встроенной функциональности

[HttpPost]
public IActionResult Post([FromBody]CreateDoctorInput createDoctorInput) {
    if (!ModelState.IsValid) {
        return BadRequest(ModelState);
    }

    //do something
}

Результат JSON будет

2 голосов
/ 20 сентября 2013

Почему бы не вернуть исходный объект ModelState клиенту, а затем использовать jQuery для чтения значений. Для меня это выглядит намного проще и использует общую структуру данных (.net's ModelState)

чтобы вернуть ModelState как Json, просто передайте его конструктору класса Json (работает с ЛЮБЫМ объектом)

C #:

return Json(ModelState);

ЯШ:

        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);
        }
2 голосов
/ 10 сентября 2010

ToDictionary - расширение Enumerable, найденное в System.Linq, упакованное в dll System.Web.Extensions http://msdn.microsoft.com/en-us/library/system.linq.enumerable.todictionary.aspx. Вот как выглядит полный класс для меня.

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

    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...