4
votes

Once in a while, we are observing this error in an ASP.NET Web API service module in a live environment and the issue gets resolved after application pool recycling. This error happened while sending the response and once the error starts happening, all the subsequent requests are failing with the same error. We couldn't reproduce this error in lower environments though. And the error occurred even with a simple GET method.

"stacktrace": " at Sy stem.Net.Http.Formatting.BaseJsonMediaTypeFormatter.CreateJsonSerializerInternal()\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.WebHost.HttpControllerHandler.d__1b.MoveNext()", "exception_class": "System.InvalidOperationException",
"exception_message": "The 'CreateJsonSerializer' method threw an exception when attempting to create a JSON serializer.

"

If anyone has come across this error, please share your thoughts.

1

1 Answers

3
votes

The problem

We recently experienced a problem with the same symptoms that have been described.

  • Web site using IIS with ASP.Net Web API on .NET Framework 4.6.1.
  • Web API requests worked fine for some time until JSON serialisation started failing causing parameter binding errors like the one described.
  • Recycling the app pool or restarting the web site fixed the problem.

The problem in our case was due to the way custom Json.Net converters were being added in the Web API controller code.

The following method of inserting custom converters was being done from controller constructors,

public FruitController() {
    GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new AppleConverter());
    GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new PearConverter());
}

This meant that for each new HTTP request context the converters were being re-added to the static converters list. This causes two issues,

  1. A memory leak where the list of converters grows over time as HTTP requests are made.
  2. A concurrency issue where multiple HTTP request context threads may attempt to write to the static list at the same time.

The second issue is what caused the problem to occur in our case.

Multiple threads were attempting to add to the same static .NET List (I.e., non-thread safe) implementation which caused a null entry in the list. The null converter entry in the list caused Json.Net to throw exceptions that resulted in similar exceptions to the one shown in the posted stack trace. All additional HTTP requests after this time failed JSON serialisation due to the null entry remaining in the converters list.

In addition to the stack trace posted, we also observed another stack trace like,

at Newtonsoft.Json.JsonSerializer.GetMatchingConverter(IList`1 converters, Type objectType) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\JsonSerializer.cs:line 1121 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.GetConverter(JsonContract contract, JsonConverter memberConverter, JsonContainerContract containerContract, JsonProperty containerProperty) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalReader.cs:line 396 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalReader.cs:line 147

The fix

To resolve the problem we removed the code that was adding the JSON converters in the Web API controller constructors and added the converters in the application start thread instead.

For example, in Global.asax,

public class WebApiApplication : HttpApplication
{
    protected void Application_Start()
    {
        /// ...

        var converters = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters;

        converters.Add(new FruitConverter());
        converters.Add(new PearConverter());
    }
}