ASP.NET MVC Cách chuyển đổi ModelState thành json


127

Làm thế nào để bạn có được một danh sách tất cả các thông báo lỗi ModelState? Tôi tìm thấy mã này để nhận tất cả các khóa: ( Trả về danh sách các khóa có lỗi ModelState )

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

Nhưng làm thế nào tôi có thể nhận được các thông báo lỗi dưới dạng IList hoặc IQueryable?

Tôi có thể đi:

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

Nhưng đó là cách làm bằng tay - chắc chắn có cách nào để làm điều đó bằng LINQ? Thuộc tính .ErrorMessage nằm cách xa chuỗi mà tôi không biết cách viết LINQ ...

Câu trả lời:


191

Bạn có thể đặt bất cứ điều gì bạn muốn vào bên trong selectmệnh đề:

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

EDIT : Bạn có thể trích xuất nhiều lỗi vào các mục danh sách riêng biệt bằng cách thêm một frommệnh đề, như thế này:

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

Hoặc là:

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

2 nd EDIT : Bạn đang tìm kiếm một Dictionary<string, string[]>:

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

Đó là một trả lời nhanh chóng :)! Này có vẻ tốt, nhưng nếu ModelState [item.Key] có nhiều hơn 1 lỗi thì sao? Lỗi [0] chỉ hoạt động đối với một thông báo lỗi duy nhất
JK.

Bạn muốn kết hợp chúng như thế nào?
SLaks

Cảm ơn đó gần như là nó - nhưng nó chọn mọi phím ngay cả khi nó không có lỗi - làm thế nào chúng ta có thể lọc ra các phím không có lỗi?
JK.

4
Thêm.Where(kvp => kvp.Value.Errors.Count > 0)
SLaks

3
Để có được đầu ra giống như Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);bạn nên sử dụng 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());Nếu không, bạn sẽ không có ExceptionMessages
Silvos

74

Dưới đây là toàn bộ thực hiện với tất cả các phần ghép lại với nhau:

Đầu tiên tạo một phương thức mở rộng:

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

Sau đó gọi phương thức mở rộng đó và trả về các lỗi từ hành động của bộ điều khiển (nếu có) là json:

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

Và cuối cùng, hiển thị các lỗi đó trên máy khách (theo kiểu jquery.validation, nhưng có thể dễ dàng thay đổi thành bất kỳ kiểu nào khác)

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

Đây có vẻ là một phương thức thú vị tuy nhiên lớp người trợ giúp không hoạt động với tôi. Đây có phải là do những thay đổi có lẽ với MVC 2? Tôi đang gặp lỗi rằng phương thức ToDipedia không tồn tại trên modelState.
Cymen

@Cymen bạn đang quên tham khảo System.Linq? ToDixi () là một phương thức mở rộng LINQ.
Nathan Taylor

8
Tùy thuộc vào sở thích của bạn .Where(m => m.Value.Count() > 0)cũng có thể được viết là .Where(m => m.Value.Any()).
Manfred

Điều này có thể được sử dụng tương tự như ModelState.ToDataSourceResult () từ Kendo.Mvc để trả về lỗi cho Grid và hiển thị thông báo lỗi khi chỉnh sửa.
malnosna

22

Tôi thích sử dụng Hashtableở đây, để tôi có được đối tượng JSON với các thuộc tính là khóa và lỗi là giá trị dưới dạng mảng chuỗi.

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

Bằng cách này bạn nhận được phản hồi sau:

{
   "success":false,
   "errors":{
      "Phone":[
         "The Phone field is required."
      ]
   }
}

8

Có rất nhiều cách khác nhau để làm điều này mà tất cả đều hoạt động. Bây giờ tôi làm điều đó ...

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

2
Bạn cũng có thể trả về BadRequest(ModelState)và nó sẽ tuần tự hóa nó thành JSON cho bạn.
Fred

6

Cách dễ nhất để làm điều này là chỉ cần trả về một BadRequestvới chính ModelState:

Ví dụ trên một PUT:

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

    // perform the update

    return StatusCode(HttpStatusCode.NoContent);
}

Nếu chúng ta sử dụng các chú thích dữ liệu trên ví dụ như một số điện thoại di động, như thế này, trong Updatelớp:

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

Điều này sẽ trả về những điều sau đây trong một yêu cầu không hợp lệ:

{
  "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."
    ]
  }
}

1
BadRequest là WebAPI cụ thể và câu hỏi này là về MVC.
rgripper

5

@JK nó đã giúp tôi rất nhiều nhưng tại sao không:

 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

Hãy xem System.Web.Http.Results.OkNegotiatedContentResult.

Nó chuyển đổi bất cứ thứ gì bạn ném vào nó thành JSON.

Vì vậy, tôi đã làm điều này

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

return Ok(errorList);

Điều này dẫn đến:

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

Tôi vẫn chưa kiểm tra điều gì xảy ra khi có nhiều hơn một lỗi cho mỗi trường nhưng điểm quan trọng là OkNegoriatedContentResult thật tuyệt vời!

Có ý tưởng linq / lambda từ @SLaks


3

Cách đơn giản đạt được điều này bằng cách sử dụng chức năng tích hợp

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

    //do something
}

Kết quả JSON sẽ là


2

ToDipedia là một tiện ích mở rộng có thể tìm thấy trong System.Linq được đóng gói trong System.Web.Extensions dll http://msdn.microsoft.com/en-us/l Library / system.linq.enumerable.todipedia.aspx . Đây là những gì lớp hoàn chỉnh đối với tôi.

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

    }

}

2

Tại sao không trả lại ModelStateđối tượng ban đầu cho máy khách, rồi sử dụng jQuery để đọc các giá trị. Đối với tôi nó trông đơn giản hơn nhiều và sử dụng cấu trúc dữ liệu chung (.net'sModelState )

để trả lại ModelState dưới dạng Json, chỉ cần chuyển nó đến hàm tạo của lớp Json (hoạt động với đối tượng BẤT K))

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

1

Biến thể với kiểu trả về thay vì trả về 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;
    }
}

0

Tôi đã tạo và tiện ích mở rộng trả về chuỗi bằng seperator "" (bạn có thể sử dụng chuỗi của riêng bạn):

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

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

        }

nếu bạn sử dụng jsonresult thì quay trở lại

return Json(Errors);

hoặc đơn giản là bạn có thể trả về modelStateErrors, tôi chưa thử. Những gì tôi đã làm là gán bộ sưu tập Lỗi cho ViewModel của tôi và sau đó lặp lại nó .. Trong trường hợp này tôi có thể trả lại Lỗi của mình qua json. Tôi có một lớp / mô hình, tôi muốn lấy nguồn / khóa nhưng tôi vẫn đang cố gắng tìm ra nó.

    public class ErrorList
{
    public string ErrorMessage;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.