JN
less than a minute read

Return all ModelState errors with field keys as json data in mvc

#linq#javascript#mvc#jquery#validation#csharp#json#modelstate#model#state

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.


Leave a comment