如何从ASP.Net MVC modelState获取所有错误?


452

我想在不知道键值的情况下从modelState中获取所有错误消息。遍历以获取ModelState包含的所有错误消息。

我怎样才能做到这一点?


5
如果您只是显示错误,那么这@Html.ValidationSummary()是一种以剃刀显示所有错误的快速方法。
levininja 2014年

11
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors)) { DoSomething(error); }
拉兹万·杜米特鲁

Answers:


529
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
错误很好,但有时您也需要modelstate的键(即字段名称)。你可以通过改变第一行是:foreach (KeyValuePair<string, ModelState> kvp in htmlHelper.ViewData.ModelState) {并插入它下面这一行:var modelState = kvp.Value;。您可以从kvp.Key
viggity,

531

使用LINQ

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

68
修改为仅返回带有错误消息的IEnumerable <string>:var allErrors = ModelState.Values.SelectMany(v => v.Errors.Select(b => b.ErrorMessage));
Kieran 2014年

6
这很棒,但是很遗憾,观看/即时窗口不支持lambda的:(
AaronLS

3
是! 我(您,任何人)都需要“使用System.Linq;” 前几名。否则,您会收到消息“值不包含“选择多个”的定义”。在我的情况下,它不见了。
Estevez 2014年

2
为什么在地狱中使用var ?????? 你不能写“ IEnumerable <ModelError>”代替吗?
HakanFıstık'6

6
@ hakam-fostok @ jb06你们都是对的。键入List<string> errors = new List<string>()而不是var errors = new List<string>()实际上是在浪费时间,但是IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);在可读性方面,在返回类型不清楚的情况下进行编写确实要好得多。(即使Visual Studio可以在鼠标悬停时将其提供给您)
2016年

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(“;”);
Tod Thomson

3
@ Tod,IEnumerable.JoinString()是您自己的扩展方法吗?请参阅stackoverflow.com/q/4382034/188926
Dunc

2
嘿Dunc-是的,我怀疑我已将该扩展方法添加到我的代码库中,并且忘记了它,然后认为它是一种框架方法大声笑:(
Tod Thomson

5
或... ModelState.Values.SelectMany(O => O.Errors).Select(O => O.ErrorMessage).Aggregate((U,V)=> U +“,” + V)
fordareh

2
当您使用Web API并返回IHttpActionResult结果时,此方法非常有用。因此,您可以这样做:return BadRequest(messages); 谢谢,邓克!
里奇·沃德


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 2012年

12

正如我发现遵循到目前为止给出的答案中的建议一样,您可以在不设置错误消息的情况下发生异常,因此要捕获所有真正需要同时获取ErrorMessage和Exception的问题。

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

}

为什么要一个包含所有错误的字符串?当你想用它做的东西在视图中没有意义,列表的排列是更好的方式恕我直言
丹尼尔TULP

1
进行调试。我的第一个问题是找出我的应用程序出了什么问题。我并不是要告诉用户只是找出问题所在。除了将示例从创建字符串枚举转换为其他枚举(例如错误消息和异常)之外,这很简单,所以真正有用的是知道您需要这两个信息
Alan Macdonald 2013年

顺便说一句,您确实意识到第二个扩展方法返回IEnumerable <String>而不只是一个大的单个字符串?
艾伦·麦克唐纳

8

如果有人想返回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业务的原因,因此引起了?在表达式的末尾。应避免出现空错误的原始表达式为: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!我在其实现下方的代码中做了一些小的更改。
Alfred Severo 2015年

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您是否看到Toto的答案?
松饼人

@TheMuffinMan是的,我有。那呢
Yasser Shaikh 2014年

@Yasser这是最好的答案。这没什么错,但是在SelectMany可用的情况下使用它毫无意义。
松饼人

4

有助于通过Json将错误消息数组传递给View:

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

4

这扩展了@Dunc的答案。查看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;            
    }
}


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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.