【问题标题】:ASP.NET MVC How to convert ModelState errors to jsonASP.NET MVC 如何将 ModelState 错误转换为 json
【发布时间】:2011-02-20 05:04:13
【问题描述】:

如何获得所有 ModelState 错误消息的列表?我找到了这段代码来获取所有密钥: (Returning a list of keys with ModelState errors)

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...

【问题讨论】:

    标签: c# asp.net-mvc linq modelstate


    【解决方案1】:

    您可以将任何内容放入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();
    

    2nd 编辑: 您正在寻找Dictionary<string, string[]>

    var errorList = ModelState.ToDictionary(
        kvp => kvp.Key,
        kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray()
    );
    

    【讨论】:

    • 这是一个快速的回复:)!嘿,看起来不错,但是如果 ModelState[item.Key] 有超过 1 个错误怎么办? Errors[0] 仅适用于单个错误消息
    • 你想如何组合它们?
    • 谢谢,差不多了 - 但即使没有错误,它也会选择每个键 - 我们如何过滤掉没有错误的键?
    • 添加.Where(kvp => kvp.Value.Errors.Count > 0)
    • 要获得与 Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState); 相同的输出,您应该使用 var errorList = modelState.Where(elem => elem.Value.Errors.Any()) .ToDictionary( kvp => kvp.Key, kvp => kvp.Value.Errors.Select(e => string.IsNullOrEmpty(e.ErrorMessage) ? e.Exception.Message : e.ErrorMessage).ToArray()); 否则您将没有 ExceptionMessages
    【解决方案2】:

    这是所有部分放在一起的完整实现:​​

    首先创建一个扩展方法:

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

    【讨论】:

    • 这看起来是一个有趣的方法,但是辅助类对我不起作用。这可能是由于 MVC 2 的变化造成的吗?我收到一个错误,即 modelState 上不存在 ToDictionary 方法。
    • @Cymen 您是否忘记引用 System.Linq? ToDictionary() 是一种 LINQ 扩展方法。
    • 根据你的喜好.Where(m =&gt; m.Value.Count() &gt; 0)也可以写成.Where(m =&gt; m.Value.Any())
    • 这可以与 Kendo.Mvc 中的 ModelState.ToDataSourceResult() 类似地使用,以将错误返回到 Grid 并在编辑时显示错误消息。
    【解决方案3】:

    我喜欢在这里使用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."
          ]
       }
    }
    

    【讨论】:

      【解决方案4】:

      最简单的方法是返回带有 ModelState 本身的 BadRequest

      例如在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."
          ]
        }
      }
      

      【讨论】:

      • BadRequest 是特定于 WebAPI 的,这个问题是关于 MVC 的。
      • 给新观众的注意事项:BadRequest 现在可以在 .NET Core 以上版本中使用
      【解决方案5】:

      有很多不同的方法可以做到这一点,所有这些都有效。这就是我现在做的......

      if (ModelState.IsValid)
      {
          return Json("Success");
      }
      else
      {
          return Json(ModelState.Values.SelectMany(x => x.Errors));
      }
      

      【讨论】:

      • 你也可以返回BadRequest(ModelState),它会为你序列化成JSON。
      【解决方案6】:

      使用内置功能的简单方法

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

      JSON result will be

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

          看看 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 非常棒!

          从@SLaks 获得了 linq/lambda 的想法

          【讨论】:

            【解决方案9】:

            ToDictionary 是在 System.Linq 中找到的可枚举扩展,打包在 System.Web.Extensions dll 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;
                    }
            
                }
            
            }
            

            【讨论】:

              【解决方案10】:

              为什么不将原始的ModelState对象返回给客户端,然后使用jQuery读取值。对我来说,它看起来更简单,并使用通用数据结构(.net 的ModelState

              要将ModelState 作为 Json 返回,只需将其传递给 Json 类构造函数(适用于任何对象)

              C#:

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

              【讨论】:

                【解决方案11】:

                返回类型的变化而不是返回 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;
                    }
                }
                

                【讨论】:

                  【解决方案12】:

                  我遇到了同样的障碍,想控制我的 400 Bad Request 输出格式,但又不想弄脏序列化 ModelState 的内容。我会使用密封(但谢天谢地公开)SerializableError 类。

                  var errorDetails = new SerializableError(ModelState);
                  
                  var errorResponse = new YourCustomResponseType
                  {
                      ModelValidationErrors = errorDetails,
                      LogMessages = new []
                      {
                          new LogMessage("Error", "Invalid model - see modelValidationErrors for detail")
                      }
                  };
                  return BadRequest(errorResponse);
                  

                  YourCustomResponseType 的位置可能如下所示:

                  public class YourCustomResponseType
                  {
                          public LogMessage[] LogMessages { get; set; }
                          public Dictionary<string, object> ModelValidationErrors { get; set; }
                  }
                  

                  SerializableErrorDictionary&lt;string, object&gt;,所以效果很好。您的回复可能如下所示:

                  {
                      "logMessages": [
                          {
                              "category": "Error",
                              "message": "Invalid model - see modelValidationErrors for detail"
                          }
                      ],
                      "modelValidationErrors": {
                          "aSettingsType.someEnumField": [
                              "The input was not valid."
                          ]
                      }
                  }
                  

                  【讨论】:

                    【解决方案13】:

                    我制作了一个返回带有分隔符“”的字符串的扩展(你可以使用你自己的):

                       public static string GetFullErrorMessage(this ModelStateDictionary modelState) {
                            var messages = new List<string>();
                    
                            foreach (var entry in modelState) {
                                foreach (var error in entry.Value.Errors)
                                    messages.Add(error.ErrorMessage);
                            }
                    
                            return String.Join(" ", messages);
                        }
                    

                    【讨论】:

                      【解决方案14】:
                        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);
                      
                              }
                      

                      如果你使用 jsonresult 则返回

                      return Json(Errors);
                      

                      或者你可以简单地返回modelStateErrors,我没试过。我所做的是将错误集合分配给我的 ViewModel,然后循环它。在这种情况下,我可以通过 json 返回我的错误。我有一个类/模型,我想获取源/密钥,但我仍在尝试弄清楚。

                          public class ErrorList
                      {
                          public string ErrorMessage;
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 2011-06-06
                        • 1970-01-01
                        • 2023-03-11
                        • 1970-01-01
                        • 1970-01-01
                        • 2010-11-24
                        • 2018-06-21
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多