ASP.NET MVC วิธีการแปลงข้อผิดพลาด ModelState เป็น json


127

คุณจะได้รับรายการข้อความแสดงข้อผิดพลาดทั้งหมดของ ModelState ได้อย่างไร? ฉันพบรหัสนี้เพื่อรับคีย์ทั้งหมด: (การส่งคืนรายการคีย์ที่มีข้อผิดพลาด ModelState )

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 อย่างไร ...

คำตอบ:


192

คุณสามารถใส่อะไรก็ได้ที่คุณต้องการภายใน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();

2 ndแก้ไข : คุณกำลังมองหาDictionary<string, string[]>:

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

ตอบกลับอย่างรวดเร็ว :)! เฮ้นั่นดูดี แต่ถ้า ModelState [item.Key] มีข้อผิดพลาดมากกว่า 1 ข้อล่ะ? ข้อผิดพลาด [0] ใช้ได้กับข้อความแสดงข้อผิดพลาดเดียว
JK

คุณต้องการรวมเข้าด้วยกันอย่างไร?
SLaks

ขอบคุณเกือบจะแล้ว - แต่การเลือกทุกคีย์แม้ว่าจะไม่มีข้อผิดพลาดเราจะกรองคีย์โดยไม่มีข้อผิดพลาดได้อย่างไร
.

4
เพิ่ม.Where(kvp => kvp.Value.Errors.Count > 0)
SLaks

3
เพื่อให้ได้ผลลัพธ์เดียวกันเช่นจาก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
Silvos

74

นี่คือการใช้งานเต็มรูปแบบกับทุกส่วนที่รวมเข้าด้วยกัน:

ขั้นแรกให้สร้างวิธีการขยาย:

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 หรือไม่? ฉันได้รับข้อผิดพลาดว่าไม่มีเมธอด ToDictionary ใน modelState
Cymen

@ ซีเมนคุณลืมอ้างอิง System.Linq หรือเปล่า? ToDictionary () เป็นวิธีการขยาย LINQ
Nathan Taylor

8
ขึ้นอยู่กับความต้องการของคุณ.Where(m => m.Value.Count() > 0)สามารถเขียนเป็น.Where(m => m.Value.Any()).
Manfred

สิ่งนี้สามารถใช้ในลักษณะเดียวกับ ModelState ToDataSourceResult () จาก Kendo.Mvc เพื่อส่งคืนข้อผิดพลาดไปยัง Grid และแสดงข้อความแสดงข้อผิดพลาดในการแก้ไข
malnosna

22

ฉันชอบใช้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."
      ]
   }
}

8

มีหลายวิธีในการทำเช่นนี้เพื่อให้ได้ผล นี่คือตอนนี้ฉันทำมัน ...

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

2
นอกจากนี้คุณยังสามารถส่งคืนBadRequest(ModelState)และจะทำให้เป็นอนุกรมเป็น JSON ให้คุณ
Fred

6

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือเพียงส่งคืนค่าBadRequestด้วย ModelState เอง:

ตัวอย่างเช่นใน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."
    ]
  }
}

1
BadRequest เป็น WebAPI เฉพาะและคำถามนี้เกี่ยวกับ MVC
rgripper

5

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

3

ลองดูที่ 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 นั้นยอดเยี่ยมมาก!

ได้รับแนวคิด linq / lambda จาก @SLaks



2

ToDictionary เป็นส่วนขยาย Enumerable พบใน 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;
        }

    }

}

2

ทำไมไม่ส่งคืนModelStateวัตถุดั้งเดิมไปยังไคลเอนต์แล้วใช้ jQuery เพื่ออ่านค่า สำหรับฉันมันดูง่ายกว่ามากและใช้โครงสร้างข้อมูลทั่วไป (.net's ModelState)

เพื่อส่งคืนModelStateเป็น Json เพียงแค่ส่งต่อไปยังตัวสร้างคลาส Json (ทำงานกับวัตถุใด ๆ )

ค#:

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

การเปลี่ยนแปลงด้วยประเภทการส่งคืนแทนที่จะส่งคืน 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

ฉันสร้างและส่วนขยายที่ส่งคืนสตริงด้วยตัวแยก "" (คุณสามารถใช้ของคุณเอง):

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

        }

ถ้าคุณใช้ jsonresult ให้กลับมา

return Json(Errors);

หรือคุณสามารถส่งคืน modelStateErrors ได้ฉันยังไม่ได้ลอง สิ่งที่ฉันทำคือกำหนดคอลเล็กชันข้อผิดพลาดให้กับ ViewModel ของฉันแล้ววนซ้ำ .. ในกรณีนี้ฉันสามารถส่งคืนข้อผิดพลาดของฉันผ่าน json ฉันมีคลาส / โมเดลฉันต้องการรับซอร์ส / คีย์ แต่ฉันยังคงพยายามคิดออก

    public class ErrorList
{
    public string ErrorMessage;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.