13

We have ASP.NET Core application which we use as a REST api. Methods use [FromBody]MyClass param as input. Problem is if the client sends an invalid JSON string the input value becomes null due to JSON serialization errors. Is there a way to log such errors?

Edit: I'm looking for application-wide solution not per-method..

dstr
  • 8,362
  • 12
  • 66
  • 106
  • 2
    Possible duplicate of [ASP.Net 5 MVC 6 - how to return invalid JSON message on POST using FromBody?](https://stackoverflow.com/questions/37277427/asp-net-5-mvc-6-how-to-return-invalid-json-message-on-post-using-frombody) – Kirk Larkin Oct 26 '17 at 07:41
  • @Kirk: This is not the same question. The linked question asks how to return json errors to the caller and the accepted answer is per-method basis solution. I'm looking to catch and log them application-wide. – dstr Oct 26 '17 at 12:01
  • 1
    Did you read the second, non-accepted answer that describes how to use a filter to do exactly what you are asking for? – Kirk Larkin Oct 26 '17 at 12:15
  • @Kirk: I did not see that, my bad. I've solved what I'm looking for differently, thanks for pointing it out. – dstr Oct 26 '17 at 13:42

2 Answers2

12

I've solved this with adding an error handler in Startup.cs:

services.AddMvc()
        .AddJsonOptions(options => {
              options.SerializerSettings.Error = (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args) =>
              {
                    //Log args.ErrorContext.Error details...
              };
        });
dstr
  • 8,362
  • 12
  • 66
  • 106
  • Yes, I agree, but how do you log it when you inject LoggerFactoryManager.Init(_); in the same ConfigureServices method? – Bjorn Bailleul Feb 06 '18 at 10:38
  • var logger = LoggerFactoryManager.GetLogger(); logger.LogWarning("Failed to deserialize JSON on input: " + args.ErrorContext.Error.Message); – Bjorn Bailleul Feb 06 '18 at 10:51
-1

You can use an action filter to capture any model binding error (including deserialization errors). You can pull the error from the ActionExecutingContext.

EDIT: To use this approach, You need to suppress the model state invalid filter because by default web api will automatically return a 400 if there is a binding error. https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.apibehavioroptions.suppressmodelstateinvalidfilter#microsoft-aspnetcore-mvc-apibehavioroptions-suppressmodelstateinvalidfilter

    public class LogModelBindingErrorFilter : IActionFilter
    {
        private readonly ILogger _logger = LogManager.GetCurrentClassLogger();

        public void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                var messages = context.ModelState.Values
                    .SelectMany(x => x.Errors)
                    .Where(x => x.Exception is JsonException)
                    .Select(x => x.ErrorMessage);

                _logger.Log(LogLevel.Error, string.Join(", ", messages));
            }
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
        }
    }

Drew Sumido
  • 162
  • 1
  • 5
  • Json deserialization error will happen before ActionFilters. Those are called after model binding. This will not be called. https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters – Pekka Ylenius Jan 06 '22 at 12:39
  • @PekkaYlenius Yes, model binding happens before action filters, but the errors that occur during model binding are stored in ModelState so it can be inspected later. https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-6.0#model-state https://stackoverflow.com/a/49245960/7810515 – Drew Sumido Feb 12 '22 at 23:25