ASP.NET MVC Bagaimana mengkonversi kesalahan ModelState ke json


127

Bagaimana Anda mendapatkan daftar semua pesan kesalahan ModelState? Saya menemukan kode ini untuk mendapatkan semua kunci: ( Mengembalikan daftar kunci dengan kesalahan ModelState )

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

Tetapi bagaimana saya bisa mendapatkan pesan kesalahan sebagai IList atau IQueryable?

Saya bisa pergi:

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

Tapi itu melakukannya secara manual - pasti ada cara untuk melakukannya menggunakan LINQ? Properti .ErrorMessage begitu jauh hingga saya tidak tahu cara menulis LINQ ...

Jawaban:


192

Anda dapat memasukkan apa pun yang Anda inginkan ke dalam selectklausa:

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

EDIT : Anda dapat mengekstraksi banyak kesalahan ke dalam item daftar terpisah dengan menambahkan fromklausa, seperti ini:

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

Atau:

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

2 nd EDIT : Anda sedang mencari Dictionary<string, string[]>:

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

Itu jawaban cepat :)! Hei itu terlihat bagus, tetapi bagaimana jika ModelState [item.Key] memiliki lebih dari 1 kesalahan? Kesalahan [0] hanya berfungsi untuk satu pesan kesalahan
JK.

Bagaimana Anda ingin menggabungkannya?
SLaks

Terima kasih sudah hampir itu - tetapi memilih setiap kunci bahkan jika tidak ada kesalahan - bagaimana kita bisa menyaring kunci tanpa kesalahan?
JK.

4
Add.Where(kvp => kvp.Value.Errors.Count > 0)
SLaks

3
Untuk mendapatkan output yang sama seperti dari Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);yang harus Anda gunakan 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());Jika tidak, Anda tidak akan memiliki ExceptionMessages
Silvos

74

Inilah implementasi penuh dengan semua bagian disatukan:

Pertama-tama buat metode ekstensi:

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

Kemudian panggil metode ekstensi itu dan kembalikan kesalahan dari tindakan pengontrol (jika ada) sebagai json:

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

Dan akhirnya, tunjukkan kesalahan tersebut di sisi klien (dalam gaya jquery.validation, tetapi dapat dengan mudah diubah ke gaya lain)

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

Ini terlihat seperti metode yang menarik namun kelas pembantu tidak bekerja untuk saya. Apakah ini karena perubahan mungkin dengan MVC 2? Saya mendapatkan kesalahan bahwa metode ToDictionary tidak ada di modelState.
Cymen

@Cymen Anda lupa referensi System.Linq? ToDictionary () adalah metode ekstensi LINQ.
Nathan Taylor

8
Tunduk pada preferensi Anda .Where(m => m.Value.Count() > 0)juga dapat ditulis sebagai .Where(m => m.Value.Any()).
Manfred

Ini dapat digunakan dengan cara yang sama sebagai ModelState.ToDataSourceResult () dari Kendo.Mvc untuk mengembalikan kesalahan ke Grid dan menampilkan pesan kesalahan dalam pengeditan.
malnosna

22

Saya suka menggunakan di Hashtablesini, sehingga saya mendapatkan objek JSON dengan properti sebagai kunci dan kesalahan sebagai nilai dalam bentuk array string.

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

Dengan cara ini Anda mendapatkan respons berikut:

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

8

Ada banyak cara berbeda untuk melakukan ini agar semuanya berfungsi. Di sini sekarang saya melakukannya ...

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

2
Anda juga dapat kembali BadRequest(ModelState)dan serialiaze ke JSON untuk Anda.
Fred

6

Cara termudah untuk melakukan ini adalah hanya mengembalikan a BadRequest dengan ModelState itu sendiri:

Misalnya pada PUT:

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

    // perform the update

    return StatusCode(HttpStatusCode.NoContent);
}

Jika kami menggunakan anotasi data pada mis. Nomor ponsel, seperti ini, di Updatekelas:

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

Ini akan mengembalikan yang berikut pada permintaan yang tidak valid:

{
  "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 adalah spesifik WebAPI dan pertanyaan ini tentang MVC.
rgripper

5

@ JK sangat membantu saya tetapi mengapa tidak:

 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

Lihatlah System.Web.Http.Results.OkNegotiatedContentResult.

Itu mengkonversi apapun yang Anda masukkan ke JSON.

Jadi saya melakukan ini

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

return Ok(errorList);

Ini menghasilkan:

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

Saya belum memeriksa apa yang terjadi ketika ada lebih dari satu kesalahan untuk setiap bidang tetapi intinya adalah OkNegoriatedContentResult brilian!

Dapatkan ide linq / lambda dari @SLaks


3

Cara sederhana untuk mencapai ini dengan menggunakan fungsionalitas bawaan

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

    //do something
}

Hasil JSON akan menjadi


2

ToDictionary adalah ekstensi Enumerable yang ditemukan di System.Linq yang dikemas dalam System.Web.Extensions dll http://msdn.microsoft.com/en-us/library/system.linq.enumerable.todictionary.aspx . Inilah yang terlihat seperti kelas lengkap bagi saya.

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

Mengapa tidak mengembalikan ModelStateobjek asli ke klien, lalu gunakan jQuery untuk membaca nilai. Bagi saya itu terlihat lebih sederhana, dan menggunakan struktur data umum (.net ModelState)

untuk mengembalikan ModelState as Json, cukup kirimkan ke konstruktor kelas Json (berfungsi dengan objek APA PUN)

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

Variasi dengan tipe pengembalian alih-alih mengembalikan 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

Saya membuat dan ekstensi yang mengembalikan string dengan "" separator (Anda dapat menggunakan milik Anda sendiri):

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

        }

jika Anda menggunakan jsonresult kemudian kembali

return Json(Errors);

atau Anda cukup mengembalikan modelStateErrors, saya belum mencobanya. Apa yang saya lakukan adalah menetapkan koleksi Kesalahan ke ViewModel saya dan kemudian loop itu .. Dalam hal ini saya dapat mengembalikan Kesalahan saya melalui json. Saya memiliki kelas / model, saya ingin mendapatkan sumber / kunci tetapi saya masih mencoba mencari tahu.

    public class ErrorList
{
    public string ErrorMessage;
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.