Skip to content

Commit 87ea03d

Browse files
martincostellopranavkm
authored andcommitted
Register SystemTextJsonResultExecutor (#11247)
Register SystemTextJsonResultExecutor as part of MVC core services so that JsonResult works without Newtonsoft.Json. Addresses #11246.
1 parent 2eca9ce commit 87ea03d

File tree

7 files changed

+262
-13
lines changed

7 files changed

+262
-13
lines changed

src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ internal static void AddMvcCoreServices(IServiceCollection services)
258258
services.TryAddSingleton<IActionResultExecutor<RedirectToRouteResult>, RedirectToRouteResultExecutor>();
259259
services.TryAddSingleton<IActionResultExecutor<RedirectToPageResult>, RedirectToPageResultExecutor>();
260260
services.TryAddSingleton<IActionResultExecutor<ContentResult>, ContentResultExecutor>();
261+
services.TryAddSingleton<IActionResultExecutor<JsonResult>, SystemTextJsonResultExecutor>();
261262
services.TryAddSingleton<IClientErrorFactory, ProblemDetailsClientErrorFactory>();
262263

263264
//

src/Mvc/test/Mvc.FunctionalTests/JsonResultTest.cs renamed to src/Mvc/test/Mvc.FunctionalTests/JsonResultWithNewtonsoftJsonTest.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.Linq;
45
using System.Net;
56
using System.Net.Http;
67
using System.Threading.Tasks;
8+
using Microsoft.AspNetCore.Hosting;
9+
using Microsoft.AspNetCore.TestHost;
10+
using Microsoft.Extensions.DependencyInjection;
711
using Xunit;
812

913
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
1014
{
11-
public class JsonResultTest : IClassFixture<MvcTestFixture<BasicWebSite.StartupWithoutEndpointRouting>>
15+
public class JsonResultWithNewtonsoftJsonTest : IClassFixture<MvcTestFixture<BasicWebSite.StartupWithNewtonsoftJson>>
1216
{
13-
public JsonResultTest(MvcTestFixture<BasicWebSite.StartupWithoutEndpointRouting> fixture)
17+
private IServiceCollection _serviceCollection;
18+
19+
public JsonResultWithNewtonsoftJsonTest(MvcTestFixture<BasicWebSite.StartupWithNewtonsoftJson> fixture)
1420
{
15-
Client = fixture.CreateDefaultClient();
21+
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(b => b.UseStartup<BasicWebSite.StartupWithNewtonsoftJson>());
22+
factory = factory.WithWebHostBuilder(b => b.ConfigureTestServices(serviceCollection => _serviceCollection = serviceCollection));
23+
24+
Client = factory.CreateDefaultClient();
1625
}
1726

1827
public HttpClient Client { get; }
@@ -21,7 +30,7 @@ public JsonResultTest(MvcTestFixture<BasicWebSite.StartupWithoutEndpointRouting>
2130
public async Task JsonResult_UsesDefaultContentType()
2231
{
2332
// Arrange
24-
var url = "http://localhost/JsonResult/Plain";
33+
var url = "http://localhost/JsonResultWithNewtonsoftJson/Plain";
2534
var request = new HttpRequestMessage(HttpMethod.Get, url);
2635

2736
// Act
@@ -42,7 +51,7 @@ public async Task JsonResult_UsesDefaultContentType()
4251
public async Task JsonResult_Conneg_Fails(string mediaType)
4352
{
4453
// Arrange
45-
var url = "http://localhost/JsonResult/Plain";
54+
var url = "http://localhost/JsonResultWithNewtonsoftJson/Plain";
4655
var request = new HttpRequestMessage(HttpMethod.Get, url);
4756
request.Headers.TryAddWithoutValidation("Accept", mediaType);
4857

@@ -61,7 +70,7 @@ public async Task JsonResult_Conneg_Fails(string mediaType)
6170
public async Task JsonResult_Null()
6271
{
6372
// Arrange
64-
var url = "http://localhost/JsonResult/Null";
73+
var url = "http://localhost/JsonResultWithNewtonsoftJson/Null";
6574
var request = new HttpRequestMessage(HttpMethod.Get, url);
6675

6776
// Act
@@ -79,7 +88,7 @@ public async Task JsonResult_Null()
7988
public async Task JsonResult_String()
8089
{
8190
// Arrange
82-
var url = "http://localhost/JsonResult/String";
91+
var url = "http://localhost/JsonResultWithNewtonsoftJson/String";
8392
var request = new HttpRequestMessage(HttpMethod.Get, url);
8493

8594
// Act
@@ -96,7 +105,7 @@ public async Task JsonResult_String()
96105
public async Task JsonResult_Uses_CustomSerializerSettings()
97106
{
98107
// Arrange
99-
var url = "http://localhost/JsonResult/CustomSerializerSettings";
108+
var url = "http://localhost/JsonResultWithNewtonsoftJson/CustomSerializerSettings";
100109
var request = new HttpRequestMessage(HttpMethod.Get, url);
101110

102111
// Act
@@ -112,7 +121,7 @@ public async Task JsonResult_Uses_CustomSerializerSettings()
112121
public async Task JsonResult_CustomContentType()
113122
{
114123
// Arrange
115-
var url = "http://localhost/JsonResult/CustomContentType";
124+
var url = "http://localhost/JsonResultWithNewtonsoftJson/CustomContentType";
116125
var request = new HttpRequestMessage(HttpMethod.Get, url);
117126

118127
// Act
@@ -125,4 +134,4 @@ public async Task JsonResult_CustomContentType()
125134
Assert.Equal("{\"message\":\"hello\"}", content);
126135
}
127136
}
128-
}
137+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Linq;
5+
using System.Net;
6+
using System.Net.Http;
7+
using System.Threading.Tasks;
8+
using Microsoft.AspNetCore.Hosting;
9+
using Microsoft.AspNetCore.TestHost;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Xunit;
12+
13+
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
14+
{
15+
public class JsonResultWithSystemTextJsonTest : IClassFixture<MvcTestFixture<BasicWebSite.StartupWithSystemTextJson>>
16+
{
17+
private IServiceCollection _serviceCollection;
18+
19+
public JsonResultWithSystemTextJsonTest(MvcTestFixture<BasicWebSite.StartupWithSystemTextJson> fixture)
20+
{
21+
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(b => b.UseStartup<BasicWebSite.StartupWithSystemTextJson>());
22+
factory = factory.WithWebHostBuilder(b => b.ConfigureTestServices(serviceCollection => _serviceCollection = serviceCollection));
23+
24+
Client = factory.CreateDefaultClient();
25+
}
26+
27+
public HttpClient Client { get; }
28+
29+
[Fact]
30+
public async Task JsonResult_UsesDefaultContentType()
31+
{
32+
// Arrange
33+
var url = "http://localhost/JsonResultWithSystemTextJson/Plain";
34+
var request = new HttpRequestMessage(HttpMethod.Get, url);
35+
36+
// Act
37+
var response = await Client.SendAsync(request);
38+
var content = await response.Content.ReadAsStringAsync();
39+
40+
// Assert
41+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
42+
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
43+
Assert.Equal("{\"message\":\"hello\"}", content);
44+
}
45+
46+
// Using an Accept header can't force Json to not be Json. If your accept header doesn't jive with the
47+
// formatters/content-type configured on the result it will be ignored.
48+
[Theory]
49+
[InlineData("application/xml")]
50+
[InlineData("text/xml")]
51+
public async Task JsonResult_Conneg_Fails(string mediaType)
52+
{
53+
// Arrange
54+
var url = "http://localhost/JsonResultWithSystemTextJson/Plain";
55+
var request = new HttpRequestMessage(HttpMethod.Get, url);
56+
request.Headers.TryAddWithoutValidation("Accept", mediaType);
57+
58+
// Act
59+
var response = await Client.SendAsync(request);
60+
var content = await response.Content.ReadAsStringAsync();
61+
62+
// Assert
63+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
64+
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
65+
Assert.Equal("{\"message\":\"hello\"}", content);
66+
}
67+
68+
// If the object is null, it will get formatted as JSON. NOT as a 204/NoContent
69+
[Fact]
70+
public async Task JsonResult_Null()
71+
{
72+
// Arrange
73+
var url = "http://localhost/JsonResultWithSystemTextJson/Null";
74+
var request = new HttpRequestMessage(HttpMethod.Get, url);
75+
76+
// Act
77+
var response = await Client.SendAsync(request);
78+
var content = await response.Content.ReadAsStringAsync();
79+
80+
// Assert
81+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
82+
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
83+
Assert.Equal("null", content);
84+
}
85+
86+
// If the object is a string, it will get formatted as JSON. NOT as text/plain.
87+
[Fact]
88+
public async Task JsonResult_String()
89+
{
90+
// Arrange
91+
var url = "http://localhost/JsonResultWithSystemTextJson/String";
92+
var request = new HttpRequestMessage(HttpMethod.Get, url);
93+
94+
// Act
95+
var response = await Client.SendAsync(request);
96+
var content = await response.Content.ReadAsStringAsync();
97+
98+
// Assert
99+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
100+
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
101+
Assert.Equal("\"hello\"", content);
102+
}
103+
104+
[Fact]
105+
public async Task JsonResult_Uses_CustomSerializerSettings()
106+
{
107+
// Arrange
108+
var url = "http://localhost/JsonResultWithSystemTextJson/CustomSerializerSettings";
109+
var request = new HttpRequestMessage(HttpMethod.Get, url);
110+
111+
// Act
112+
var response = await Client.SendAsync(request);
113+
var content = await response.Content.ReadAsStringAsync();
114+
115+
// Assert
116+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
117+
Assert.Equal("{\"Message\":\"hello\"}", content);
118+
}
119+
120+
[Fact]
121+
public async Task JsonResult_CustomContentType()
122+
{
123+
// Arrange
124+
var url = "http://localhost/JsonResultWithSystemTextJson/CustomContentType";
125+
var request = new HttpRequestMessage(HttpMethod.Get, url);
126+
127+
// Act
128+
var response = await Client.SendAsync(request);
129+
var content = await response.Content.ReadAsStringAsync();
130+
131+
// Assert
132+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
133+
Assert.Equal("application/message+json", response.Content.Headers.ContentType.MediaType);
134+
Assert.Equal("{\"message\":\"hello\"}", content);
135+
}
136+
}
137+
}

src/Mvc/test/WebSites/BasicWebSite/Controllers/JsonResultController.cs renamed to src/Mvc/test/WebSites/BasicWebSite/Controllers/JsonResultWithNewtonsoftJsonController.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88

99
namespace BasicWebSite.Controllers
1010
{
11-
public class JsonResultController : Controller
11+
public class JsonResultWithNewtonsoftJsonController : Controller
1212
{
1313
private static readonly JsonSerializerSettings _customSerializerSettings;
1414

15-
static JsonResultController()
15+
static JsonResultWithNewtonsoftJsonController()
1616
{
1717
_customSerializerSettings = JsonSerializerSettingsProvider.CreateSerializerSettings();
1818
_customSerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
@@ -45,4 +45,4 @@ public JsonResult String()
4545
return new JsonResult("hello");
4646
}
4747
}
48-
}
48+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Text.Json;
5+
using Microsoft.AspNetCore.Mvc;
6+
7+
namespace BasicWebSite.Controllers
8+
{
9+
public class JsonResultWithSystemTextJsonController : Controller
10+
{
11+
private static readonly JsonSerializerOptions _customSerializerSettings;
12+
13+
static JsonResultWithSystemTextJsonController()
14+
{
15+
_customSerializerSettings = new JsonSerializerOptions();
16+
}
17+
18+
public JsonResult Plain()
19+
{
20+
return new JsonResult(new { Message = "hello" });
21+
}
22+
23+
public JsonResult CustomContentType()
24+
{
25+
var result = new JsonResult(new { Message = "hello" });
26+
result.ContentType = "application/message+json";
27+
return result;
28+
}
29+
30+
public JsonResult CustomSerializerSettings()
31+
{
32+
return new JsonResult(new { Message = "hello" }, _customSerializerSettings);
33+
}
34+
35+
public JsonResult Null()
36+
{
37+
return new JsonResult(null);
38+
}
39+
40+
public JsonResult String()
41+
{
42+
return new JsonResult("hello");
43+
}
44+
}
45+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
namespace BasicWebSite
9+
{
10+
public class StartupWithNewtonsoftJson
11+
{
12+
public void ConfigureServices(IServiceCollection services)
13+
{
14+
services
15+
.AddMvc()
16+
.SetCompatibilityVersion(CompatibilityVersion.Latest)
17+
.AddNewtonsoftJson();
18+
}
19+
20+
public void Configure(IApplicationBuilder app)
21+
{
22+
app.UseDeveloperExceptionPage();
23+
24+
app.UseRouting();
25+
26+
app.UseEndpoints((endpoints) => endpoints.MapDefaultControllerRoute());
27+
}
28+
}
29+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
namespace BasicWebSite
9+
{
10+
public class StartupWithSystemTextJson
11+
{
12+
public void ConfigureServices(IServiceCollection services)
13+
{
14+
services
15+
.AddMvc()
16+
.SetCompatibilityVersion(CompatibilityVersion.Latest);
17+
}
18+
19+
public void Configure(IApplicationBuilder app)
20+
{
21+
app.UseDeveloperExceptionPage();
22+
23+
app.UseRouting();
24+
25+
app.UseEndpoints((endpoints) => endpoints.MapDefaultControllerRoute());
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)