Convert ModelState to JSON for Logging

How does it convert ModelState to JSON?

The SerializableError Class provides this functionality.

And more importantly, how can I use what it is using?

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

//get key(s) and error message(s) from the ModelState
var serializableModelState = new SerializableError(ModelState);

//convert to a string
var modelStateJson = JsonConvert.SerializeObject(serializableModelState);

//log it
logger.LogInformation("Bad Model State", modelStateJson);

Sample output

{
    "Name": ["The Name field is required."]
}

I was able to figure this out by digging through the ASP.NET Core MVC source code. The interesting part of source code from the SerializableError class:

foreach (var keyModelStatePair in modelState)
{
    var key = keyModelStatePair.Key;
    var errors = keyModelStatePair.Value.Errors;
    if (errors != null && errors.Count > 0)
    {
        var errorMessages = errors.Select(error =>
        {
            return string.IsNullOrEmpty(error.ErrorMessage) ?
                Resources.SerializableError_DefaultError : error.ErrorMessage;
        }).ToArray();

        Add(key, errorMessages);
    }
}

Just to get a list of the error messages for each Model Property that failed validation in the manner you want as indicated above using an extension method. i.e ModelState.ToJson(), you need create a static class with a static function ToJson(...). The code example will look something like this.

public static class ModelStateExtensions
{
    /// <summary>
    /// Reads all the error messages in a <see cref="ModelStateDictionary"/> as 
    /// a collection and returns a JSON <see cref="string"/> of the list.
    /// </summary>
    /// <param name="modelstate">Current modelstate assuming that you've checked
    /// and confirmed that is Invalid using <see 
    /// cref="ModelStateDictionary.IsValid"/>
    /// </param>
    /// <returns>
    /// Collection of validation errors for the model as a JSON string.
    /// </returns>
    public static string ToJson(this ModelStateDictionary modelstate)
    {
        List<string> errors = modelstate.Values
                                        .SelectMany(x => x.Errors)
                                        .Select(x => x.ErrorMessage)
                                        .ToList();
        return JsonConvert.SerializeObject(errors);
    }
}

The ModelState property on every controller is normally a ModelStateDictionary, so if we want an additional method for it, that is the class we need to extend. You can learn more about extension methods in C# by following this Link.

Let now see how to use our extension method in a sample controller action:

public IActionResult Create(UserViewModel model)
{
    if(!ModelState.IsValid)
    {
        string json = ModelState.ToJson();

        // insert code to log json to file here

        return BadRequest(ModelState);
    }
}