ASP.NET MVC Как преобразовать ошибки ModelState в json
Как вы получаете список всех сообщений об ошибках 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...
Ответы
Ответ 1
Вы можете поместить все, что хотите, в предложение select
:
var errorList = (from item in ModelState
where item.Value.Errors.Any()
select item.Value.Errors[0].ErrorMessage).ToList();
EDIT. Вы можете извлечь несколько ошибок в отдельные элементы списка, добавив предложение 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 EDIT:
Вы ищете Dictionary<string, string[]>
:
var errorList = ModelState.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray()
);
Ответ 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);
}
И, наконец, покажите эти ошибки на clientide (в стиле 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());
}
}
Ответ 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
Есть много разных способов сделать это, чтобы все работало. Вот теперь я делаю это...
if (ModelState.IsValid)
{
return Json("Success");
}
else
{
return Json(ModelState.Values.SelectMany(x => x.Errors));
}
Ответ 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;
}
Ответ 6
Взгляните на 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
Ответ 7
Самый простой способ сделать это - просто вернуть 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."
]
}
}
Ответ 8
ToDictionary - это Enumerable extension, найденное в 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;
}
}
}
Ответ 9
Почему бы не вернуть исходный объект ModelState
клиенту, а затем использовать jQuery для чтения значений. Для меня это выглядит намного проще и использует общую структуру данных (.net 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);
}
Ответ 10
Простой способ добиться этого, используя встроенные функции
[HttpPost]
public IActionResult Post([FromBody]CreateDoctorInput createDoctorInput) {
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
//do something
}
Результат JSON будет
Ответ 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
Я сделал и расширение, которое возвращает строку с seperator "" (вы можете использовать свои собственные):
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);
}
Ответ 13
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, я не пробовал его. То, что я сделал, это назначить коллекцию Errors для моей модели ViewModel, а затем зациклировать ее. В этом случае я могу вернуть свои ошибки через json. У меня есть класс/модель, я хотел получить источник/ключ, но я все еще пытаюсь понять это.
public class ErrorList
{
public string ErrorMessage;
}