Aspnetcore: JsonResult and Controller.Json() do not work without calling AddNewtonsoftJson() in 3.0 preview 6

Created on 15 Jun 2019  路  8Comments  路  Source: dotnet/aspnetcore

Describe the bug

If AddNewtonsoftJson() from Microsoft.AspNetCore.Mvc.NewtonsoftJson is not called while configuring MVC in ASP.NET Core 3.0 preview 6, then rendering the result of a call to the Controller.Json() method fails with an InvalidOperationException and an HTTP 500 status.

InvalidOperationException: No service for type 'Microsoft.AspNetCore.Mvc.Infrastructure.IActionResultExecutor`1[Microsoft.AspNetCore.Mvc.JsonResult]' has been registered.
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<T>(IServiceProvider provider)
Microsoft.AspNetCore.Mvc.JsonResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultAsync(IActionResult result)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeNextResultFilterAsync<TFilter, TFilterAsync>()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Searching for SystemTextJsonResultExecutor suggests that the built-in SystemTextJsonResultExecutor is never registered with the service collection and was missed as part of the work for #9554.

As the type is internal, it's not possible to manually register the class so the only workarounds are to provide a custom implementation or to use the Newtonsoft.Json implementation via AddNewtonsoftJson() instead.

To Reproduce

A solution that reproduces this issue is available here: https://github.com/martincostello/Missing-SystemTextJsonResultExecutor-Repro

The relevant code is:

public class HomeController : Controller
{
    public IActionResult Index()
        => Json(new { message = "Test" });
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        var builder = services
            .AddControllersWithViews()
            .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
            .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);

        // Uncomment the line below to get the application to work
        // builder.AddNewtonsoftJson();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseDeveloperExceptionPage();

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
        });
    }
}

Expected behavior

An HTTP request to http://localhost:5000 returns {"message":"Test"} with an HTTP 200 status code.

Additional context

.NET Core SDK (reflecting any global.json):
 Version:   3.0.100-preview6-012264
 Commit:    be3f0c1a03

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18362
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.0.100-preview6-012264\

Host (useful for support):
  Version: 3.0.0-preview6-27804-01
  Commit:  fdf81c6faf
Done area-mvc bug

Most helpful comment

Users can work around the issue with the following snippet:

services.Add(
    new ServiceDescriptor(
        typeof(IActionResultExecutor<JsonResult>),
        Type.GetType("Microsoft.AspNetCore.Mvc.Infrastructure.SystemTextJsonResultExecutor, Microsoft.AspNetCore.Mvc.Core"),
        ServiceLifetime.Singleton));

All 8 comments

Users can work around the issue with the following snippet:

services.Add(
    new ServiceDescriptor(
        typeof(IActionResultExecutor<JsonResult>),
        Type.GetType("Microsoft.AspNetCore.Mvc.Infrastructure.SystemTextJsonResultExecutor, Microsoft.AspNetCore.Mvc.Core"),
        ServiceLifetime.Singleton));

Thanks. Works like a charm.
I nearly threw my PC out of the window trying to solve it

Confirm the issue - experiencing the same.

Note: works OK if I add
services.AddNewtonsoftJson(options => options.SerializerSettings.Formatting = Formatting.Indented)

doesn't work if I add
services..AddJsonOptions(options => { options.JsonSerializerOptions.WriteIndented = true; })

is there any way to do this without NewtonSoft.Json?

@Tvde1 Yes, you can use the workaround stated above: https://github.com/aspnet/AspNetCore/issues/11246#issuecomment-502381495

Just as a FYI I am hitting this issue when trying to accept plain Object for input using System.Text.Json using ASP.Net Core 3.0 (Latest preview)

I have a controller with a method as follows:

public ActionResult PutMapData(string parameterFromUrl, [FromBody]ApiInput<Object> inputFromBody) {...}

Without the workaround posted by @martincostello I experience the same exception outlined which resulted in this issue being created.

Again, I have it working by supplying the workaround outlined by @martincostello however I feel like this should not be something I should have to do.

If I should have defined the input as something other than Object to get this to work straight out of the box then please direct me to where I can read that bit of information.

The justification for Object rather than a strongly typed Type is because that this particular end point accepts a raw json object which can be null or have a varying amount of properties and as the input data is valid it should be accepted.

Dot Net Core Info:

.NET Core SDK (reflecting any global.json):
Version: 3.0.100-preview6-012264
Commit: be3f0c1a03

Runtime Environment:
OS Name: Windows
OS Version: 6.1.7601
OS Platform: Windows
RID: win7-x64
Base Path: C:\Program Files\dotnet\sdk\3.0.100-preview6-012264\

Host (useful for support):
Version: 3.0.0-preview6-27804-01
Commit: fdf81c6faf

Visual Studio Info:

Microsoft Visual Studio Enterprise 2019 Preview
Version 16.2.0 Preview 4.0
VisualStudio.16.Preview/16.2.0-pre.4.0+29111.141

Hi, it looks like you are posting on a closed issue/PR/commit!

We're very likely to lose track of your bug/feedback/question unless you:

  1. Open a new issue
  2. Explain very clearly what you need help with
  3. If you think you have found a bug, include detailed repro steps so that we can investigate the problem

Thanks!

FYI this issue is fixed in preview 7, which was released today.

Was this page helpful?
0 / 5 - 0 ratings