I was recently validating a jquery form submission and wanted to return all the model state errors as json back to the page. Retrieving all model state errors isn't as simple as you might think since on each field can contain multiple errors. Here is how I did it.
Define a class to hold each model error
public class Error
{
public Error(string key, string message)
{
Key = key;
Message = message;
}
public string Key{ get; set; }
public string Message { get; set; }
}
Create an extension method to retrieve the errors
Because we are using Linq to retrieve the errors, we can either create the extension method using Method syntax or Query syntax. Both solutions are provided below.
Option 1: Uses Method syntax. It's a bit longer but arguably more readable.
public static class ModelStateExtensions
{
public static IEnumerable<Error> AllErrors(this ModelStateDictionary modelState)
{
var result = new List<Error>();
var erroneousFields = modelState.Where(ms => ms.Value.Errors.Any())
.Select(x => new { x.Key, x.Value.Errors });
foreach (var erroneousField in erroneousFields)
{
var fieldKey = erroneousField.Key;
var fieldErrors = erroneousField.Errors
.Select(error => new Error(fieldKey, error.ErrorMessage));
result.AddRange(fieldErrors);
}
return result;
}
}
Option 2: Uses Query syntax. This solution is more compact but possible less readable.
public static IEnumerable<Error> AllErrors(this ModelStateDictionary modelState)
{
var result = from ms in modelState
where ms.Value.Errors.Any()
let fieldKey = ms.Key
let errors = ms.Value.Errors
from error in errors
select new Error(fieldKey, error.ErrorMessage);
return result;
}
Using the extension method in a controller
Using the extension method is simple
/**** IMPORTANT: Don't forget to add a using statement to use your extension method *****/
[HttpPost]
public ActionResult PostAction(MyViewModel viewModel)
{
if (ModelState.IsValid)
{
// Perform logic
return new Content("success");
}
//set error status
Response.StatusCode = 400;
// Important for live environment.
Response.TrySkipIisCustomErrors = true;
var modelErrors = ModelState.AllErrors(); // <<<<<<<<< SEE HERE
return Json(modelErrors);
}
Notice the
Response.TrySkipIisCustomErrors = true;line.
This caught me out when I released this code to a live environment, as by default setting status code overwrites customErrors. See this stack overflow question for more information
Reading the errors
All that is left to do now is set up our javascript method to read our json result.
$.post(postUrl, postData)
...
.fail(function (error) {
var response = JSON.parse(error.responseText);
for (var i = 0; i < response.length; i++) {
var error = response[i];
var fieldKey = error.Key;
var message = error.Message;
// apply custom logic with field keys and messages
console.log(fieldKey + ': ' + message);
}
}
I hope you find this helpful, please tweet this article using the links above or comment using the form below.