Як отримати всі помилки з ASP.Net MVC modelState?


453

Я хочу винести всі повідомлення про помилки з моделіState, не знаючи ключових значень. Прокручуючи, щоб схопити всі повідомлення про помилки, які містить ModelState.

Як я можу це зробити?


5
Якщо ви просто відображаєте помилки, то @Html.ValidationSummary()це швидкий спосіб їх відображення у бритві.
levininja

11
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }
Разван Думітру

Відповіді:


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

Дивіться також Як отримати колекцію помилок стану моделі в ASP.NET MVC? .


22
Дуже корисний. Зауважте, що в деяких сценаріях, таких як помилки прив'язки та невдалі запити, будуть записи ModelState з порожнім рядком для, Value.ErrorMessageа замість цьогоValue.Exception.Message
AaronLS

5
Помилки є приємними, але іноді вам також потрібен ключ модельного стану (тобто назва поля). ви можете отримати , що, змінивши перший рядок на це: foreach (KeyValuePair<string, ModelState> kvp in htmlHelper.ViewData.ModelState) {і вставити цей рядок під ним: var modelState = kvp.Value;. Ключ ви можете отримати відkvp.Key
viggity

534

Використання LINQ :

IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);

69
Змінено для повернення IEnumerable <string> із лише повідомленням про помилку :: var allErrors = ModelState.Values.SelectMany (v => v.Errors.Select (b => b.ErrorMessage));
Киеран

6
Це чудово, але, на жаль, вікна Watch /
Nemediate

3
Так! Мені (вам, будь-кому) потрібно "використовувати System.Linq;" вгорі. В іншому випадку ви отримали повідомлення "Значення не містить визначення для Вибір багатьох". У моєму випадку це було відсутнє.
Естевес

2
чому в пеклі, використовуючи var ?????? не могли ви замість цього написати "IEnumerable <ModelError>" ???
Hakan Fıstık

6
@ hakam-fostok @ jb06 ви обоє праві. Введення тексту List<string> errors = new List<string>()замість справді var errors = new List<string>()є марною витратою часу, але написання IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);, де тип повернення не зовсім зрозумілий, дійсно більший з точки зору читабельності. (навіть якщо візуальна студія може надати вам її на наведення миші)
квітня

192

Спираючись на версію LINQ, якщо ви хочете об'єднати всі повідомлення про помилки в один рядок:

string messages = string.Join("; ", ModelState.Values
                                        .SelectMany(x => x.Errors)
                                        .Select(x => x.ErrorMessage));

5
Інший варіант - зробити наступне: ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .JoinString (";");
Тод Томсон

3
@Tod, це IEnumerable.JoinString () ваш власний метод розширення? Дивіться stackoverflow.com/q/4382034/188926
Данк

2
Ей Данк - так, я підозрюю, що я додав цей метод розширення до своєї кодової бази і забув про нього, а потім подумав, що це рамковий метод LOL :(
Тод Томсон,

5
або ... ModelState.Values.SelectMany (O => O.Errors) .Select (O => O.ErrorMessage) .Агрегат ((U, V) => U + "," + V)
fordareh

2
Це чудово працює, коли ви використовуєте веб-api та повертаєте результат IHttpActionResult. Отже, ви можете просто зробити: повернути BadRequest (повідомлення); Дякую, Данку!
Rich Ward

32

Я зміг це зробити за допомогою невеликого 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;
}

Вищеописаний метод повертає список помилок перевірки.

Подальше читання :

Як прочитати всі помилки з ModelState в ASP.NET MVC


17

Під час налагодження я вважаю корисним розмістити таблицю внизу кожної моєї сторінки, щоб показати всі помилки 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

12

Як я виявив, дотримуючись порад у відповідях, наведених до цих пір, ви можете отримати винятки, що виникають без встановлення повідомлень про помилки, тож, щоб вирішити всі проблеми, які вам справді потрібно отримати як ErrorMessage, так і виняток.

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

або як метод розширення

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

}

чому ви хочете рядок з усіма помилками в ньому? не має сенсу, коли ви хочете щось зробити з цим у перегляді, масив списку набагато краще imho
Daniël Tulp

1
Для налагодження. Першою моєю проблемою було з’ясувати, що сталося не так у моєму додатку. Я не намагався сказати користувачеві просто дізнатися, що пішло не так. Крім того, тривіально конвертувати цей приклад із створення перерахування рядків до перерахування чогось іншого, наприклад, повідомлення про помилку та виняток, тому справді корисною справою є знання того, що вам потрібні обидва біти інформації
Алан Макдональд

До речі, ти зрозумів, що другий метод розширення повертає IEnumerable <String>, а не лише велику одиночну рядок?
Алан Макдональд

8

У випадку, якщо хтось хоче повернути властивість Name of Model для прив'язки повідомлення про помилку у сильно набраному вигляді.

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

Таким чином ви можете фактично пов'язати помилку з полем, яке викинуло помилку.


7

Виведення лише повідомлень про помилки для мене було недостатньо, але це зробило трюк.

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

1
Як застереження, пара ключових значень у ModelState може включати значення NULL, саме тому тут оригінальний код включав милий C # 6 бізнес з оператором null-coalesce (?.), Отже, і до ?? в кінці виразу. Оригінальним виразом, який повинен захищати від нульових помилок, було: state.Value.?AttemptedValue ?? "[НУЛЬ]". Наскільки я знаю, код у його нинішньому стані, без підступного поводження з випадками, коли state.Value == null, ризикує.
Джош Саттерфілд

5

На всякий випадок, коли комусь це потрібно, я зробив і використовую наступний статичний клас у своїх проектах

Приклад використання:

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

Вживання:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using WebGrease.Css.Extensions;

Клас:

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 !! Я вніс невелику зміну коду нижче його реалізації.
Альфред Северо

4

І це теж працює:

var query = from state in ModelState.Values
    from error in state.Errors
    select error.ErrorMessage;
var errors = query.ToArray(); // ToList() and so on...

@Yasser Ви бачили відповідь Тото?
Людина-булочка

@TheMuffinMan так, я маю. Що з цим?
Ясер Шейх

@Yasser Це найкраща відповідь. Нічого поганого в цьому немає, але немає сенсу використовувати його, коли SelectManyвін є.
Людина-булочка

4

Корисно для передачі масиву повідомлень про помилки до View, можливо, через Json:

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

4

Це розширюється після відповіді від @Dunc. Дивіться коментарі xml doc

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

3

Крім того, він ModelState.Values.ErrorMessageможе бути порожнім, але ModelState.Values.Exception.Messageможе вказувати на помилку.


0

У вашій реалізації вам не вистачає статичного класу, таким має бути.

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

скоріше

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.