ChatGPT解决这个技术问题 Extra ChatGPT

ASP.NET MVC 如何将 ModelState 错误转换为 json

如何获得所有 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 ......


S
SLaks

您可以将 anything 放入 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
K
Kaushik Thanki

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

首先创建一个扩展方法:

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 => m.Value.Count() > 0) 也可以写成 .Where(m => m.Value.Any())
这可以与 Kendo.Mvc 中的 ModelState.ToDataSourceResult() 类似地使用,以将错误返回到 Grid 并在编辑时显示错误消息。
J
Jovica Zaric

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

D
Dean North

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

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

您也可以返回 BadRequest(ModelState),它会为您将其序列化为 JSON。
E
Erik A. Brandstadmoen

最简单的方法是只返回带有 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 上使用
s
spottedmahn

通过使用内置功能实现此目的的简单方法

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

    //do something
}

JSON result will be


h
h45d6f7d4f6f

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

A
Andy

看看 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 的想法


p
philrabin

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

    }

}

J
Jason Aller

为什么不将原始的 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);
        }

J
Jeff Circeo

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

D
David Peters

我遇到了同样的障碍,想控制我的 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<string, object>,所以效果很好。您的回复可能如下所示:

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

N
Niyaz Mukhamedya

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

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

p
ping

您可以为此使用中间件:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var result = new ValidationFailedResult(context.ModelState);
        result.ContentTypes.Add(MediaTypeNames.Application.Json);
        return result;
    };
});

和验证失败结果:

public class ValidationFailedResult : ObjectResult
    {
        public ValidationFailedResult(ModelStateDictionary modelState)
            : base(new ErrorResponse(modelState.Keys
                     .SelectMany(key => modelState[key].Errors.Select(x => new ApplicationError(StatusCodes.Status422UnprocessableEntity, key, x.ErrorMessage)))
                     .ToList()))
        {
            StatusCode = StatusCodes.Status422UnprocessableEntity; //change the http status code to 422.
        }
    }

和应用程序错误:

 public class ApplicationError
    {
        public ApplicationError(int code, string title, string detail)
        {
            Code=code;
            Title=title;
            Detail=detail;
        }

        public int Code { get; set; }
        public string Title { get; set; }
        public string Detail { get; set; }

        public override string ToString()
        {
            return $"Status:{Code} Title:{Title} Detail:{Detail}";
        }
    }

结果如下所示:

{
    "errors": [
        {
            "code": 422,
            "title": "$",
            "detail": "'\"' is invalid after a value. Expected either ',', '}', or ']'. Path: $ | LineNumber: 7 | BytePositionInLine: 1."
        },
        {
            "code": 422,
            "title": "input",
            "detail": "The input field is required."
        }
    ]
}

来源:https://docs.microsoft.com/en-us/answers/questions/620570/net-core-web-api-model-validation-error-response-t.html


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