ASP.NET MVC How to convert ModelState errors to json

C#asp.net MvcLinqModelstate

C# Problem Overview


How do you get a list of all ModelState error messages? I found this code to get all the keys: ( https://stackoverflow.com/questions/888521/returning-a-list-of-keys-with-modelstate-errors)

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

But how would I get the error messages as a IList or IQueryable?

I could go:

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

But thats doing it manually - surely there is a way to do it using LINQ? The .ErrorMessage property is so far down the chain that I don't know how to write the LINQ...

C# Solutions


Solution 1 - C#

You can put anything you want to inside the select clause:

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

EDIT: You can extract multiple errors into separate list items by adding a from clause, like this:

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

Or:

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

2nd EDIT: You're looking for a Dictionary<string, string[]>:

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

Solution 2 - C#

Here is the full implementation with all the pieces put together:

First create an extension method:

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

Then call that extension method and return the errors from the controller action (if any) as json:

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

And then finally, show those errors on the clientside (in jquery.validation style, but can be easily changed to any other style)

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

Solution 3 - C#

I like to use Hashtable here, so that I get JSON object with properties as keys and errors as value in form of string array.

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

This way you get following response:

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

Solution 4 - C#

The easiest way to do this is to just return a BadRequest with the ModelState itself:

For example on a PUT:

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

    // perform the update

    return StatusCode(HttpStatusCode.NoContent);
}

If we use data annotations on e.g. a mobile number, like this, in the Update class:

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

This will return the following on an invalid request:

{
  "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."
    ]
  }
}

Solution 5 - C#

There are lots of different ways to do this that all work. Here is now I do it...

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

Solution 6 - C#

Simple way achieve this by using built-in functionality

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

	//do something
}

JSON result will be

Solution 7 - C#

@JK it helped me a lot but why not:

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

Solution 8 - C#

Take a look at System.Web.Http.Results.OkNegotiatedContentResult.

It converts whatever you throw into it to JSON.

So I did this

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

return Ok(errorList);

This resulted in:

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

I am yet to check what happens when there is more than one error for each field but the point is the OkNegoriatedContentResult is brilliant!

Got the linq/lambda idea from @SLaks

Solution 9 - C#

ToDictionary is an Enumerable extension found in System.Linq packaged in the System.Web.Extensions dll http://msdn.microsoft.com/en-us/library/system.linq.enumerable.todictionary.aspx. Here's what the complete class looks like for me.

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

Solution 10 - C#

Why not return the original ModelState object to the client, and then use jQuery to read the values. To me it looks much simpler, and uses the common data structure (.net's ModelState)

to return the ModelState as Json, simply pass it to Json class constructor (works with ANY object)

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

Solution 11 - C#

Variation with return type instead of returning 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;
    }
}

Solution 12 - C#

I ran into the same hurdle, wanting to control my 400 Bad Request output format but not wanting to get my hands dirty serializing the guts of ModelState. I would up using the sealed (but public, thankfully) SerializableError class.

var errorDetails = new SerializableError(ModelState);

var errorResponse = new YourCustomResponseType
{
    ModelValidationErrors = errorDetails,
    LogMessages = new []
    {
        new LogMessage("Error", "Invalid model - see modelValidationErrors for detail")
    }
};
return BadRequest(errorResponse);

Where YourCustomResponseType might look like this:

public class YourCustomResponseType
{
        public LogMessage[] LogMessages { get; set; }
        public Dictionary<string, object> ModelValidationErrors { get; set; }
}

SerializableError is a Dictionary<string, object> so this works out nicely. Your response might look like this:

{
    "logMessages": [
        {
            "category": "Error",
            "message": "Invalid model - see modelValidationErrors for detail"
        }
    ],
    "modelValidationErrors": {
        "aSettingsType.someEnumField": [
            "The input was not valid."
        ]
    }
}

Solution 13 - C#

I made and extension that returns string with seperator " " (you can use your own):

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

Solution 14 - C#

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

if you use jsonresult then return

return Json(Errors);

or you can simply return the modelStateErrors, I havent tried it. What I did is assign the Errors collection to my ViewModel and then loop it..In this case I can return my Errors via json. I have a class/model, I wanted to get the source/key but I'm still trying to figure it out.

    public class ErrorList
{
    public string ErrorMessage;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJK.View Question on Stackoverflow
Solution 1 - C#SLaksView Answer on Stackoverflow
Solution 2 - C#JK.View Answer on Stackoverflow
Solution 3 - C#Jovica ZaricView Answer on Stackoverflow
Solution 4 - C#Erik A. BrandstadmoenView Answer on Stackoverflow
Solution 5 - C#Dean NorthView Answer on Stackoverflow
Solution 6 - C#NisfanView Answer on Stackoverflow
Solution 7 - C#h45d6f7d4f6fView Answer on Stackoverflow
Solution 8 - C#AndyView Answer on Stackoverflow
Solution 9 - C#philrabinView Answer on Stackoverflow
Solution 10 - C#d.popovView Answer on Stackoverflow
Solution 11 - C#Jeff CirceoView Answer on Stackoverflow
Solution 12 - C#David PetersView Answer on Stackoverflow
Solution 13 - C#Niyaz MukhamedyaView Answer on Stackoverflow
Solution 14 - C#CyberNinjaView Answer on Stackoverflow