From d6012d4297cedf0fbfd15c051d3773c3462788ea Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Fri, 8 May 2015 13:17:53 -0700 Subject: [PATCH] [Fixes #2506] Added overload to Controller.Json to accept JsonSerializerSettings --- .../ActionResults/JsonResult.cs | 16 ++++++++-- src/Microsoft.AspNet.Mvc.Core/Controller.cs | 24 +++++++++++++++ .../ControllerTests.cs | 19 ++++++++++++ .../ControllerUnitTestabilityTests.cs | 30 ++++++++++++++++++- .../JsonResultTest.cs | 27 +++++++++++++++++ .../JsonResultTest.cs | 20 +++++++++++++ .../Controllers/JsonResultController.cs | 16 ++++++++++ 7 files changed, 149 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs index ccf9cba086..3318b2c5d6 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs @@ -9,6 +9,7 @@ using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Internal; using Microsoft.Framework.OptionsModel; using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; namespace Microsoft.AspNet.Mvc { @@ -27,7 +28,7 @@ namespace Microsoft.AspNet.Mvc }; /// - /// Creates a new with the given . + /// Creates a new with the given . /// /// The value to format as JSON. public JsonResult(object value) @@ -36,7 +37,18 @@ namespace Microsoft.AspNet.Mvc } /// - /// Creates a new with the given . + /// Creates a new with the given . + /// + /// The value to format as JSON. + /// The to be used by + /// the formatter. + public JsonResult(object value, [NotNull] JsonSerializerSettings serializerSettings) + : this(value, formatter: new JsonOutputFormatter { SerializerSettings = serializerSettings }) + { + } + + /// + /// Creates a new with the given . /// /// The value to format as JSON. /// The formatter to use, or null to choose a formatter dynamically. diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs index 2cf4f110da..d2b51b1b8a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs @@ -15,6 +15,7 @@ using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Routing; using Microsoft.Framework.Internal; using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; namespace Microsoft.AspNet.Mvc { @@ -399,6 +400,29 @@ namespace Microsoft.AspNet.Mvc return new JsonResult(data); } + /// + /// Creates a object that serializes the specified object + /// to JSON. + /// + /// The object to serialize. + /// The to be used by + /// the formatter. + /// The created that serializes the specified + /// as JSON format for the response. + /// Callers should cache an instance of to avoid + /// recreating cached data with each call. + [NonAction] + public virtual JsonResult Json(object data, [NotNull] JsonSerializerSettings serializerSettings) + { + var disposableValue = data as IDisposable; + if (disposableValue != null) + { + Response.OnResponseCompleted(_ => disposableValue.Dispose(), state: null); + } + + return new JsonResult(data, serializerSettings); + } + /// /// Creates a object that redirects to the specified . /// diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs index 72625e3eb7..e60daa5f6d 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs @@ -19,6 +19,7 @@ using Microsoft.AspNet.WebUtilities; #if DNX451 using Moq; #endif +using Newtonsoft.Json; using Xunit; namespace Microsoft.AspNet.Mvc.Test @@ -1031,6 +1032,24 @@ namespace Microsoft.AspNet.Mvc.Test Assert.Same(data, actualJsonResult.Value); } + [Fact] + public void Controller_Json_WithParameterValueAndSerializerSettings_SetsRespectiveValues() + { + // Arrange + var controller = new TestableController(); + var data = new object(); + var serializerSettings = new JsonSerializerSettings(); + + // Act + var actualJsonResult = controller.Json(data, serializerSettings); + + // Assert + Assert.IsType(actualJsonResult); + Assert.Same(data, actualJsonResult.Value); + var jsonFormatter = actualJsonResult.Formatter as JsonOutputFormatter; + Assert.Same(serializerSettings, jsonFormatter.SerializerSettings); + } + [Fact] public void Controller_Json_IDisposableObject_RegistersForDispose() { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerUnitTestabilityTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerUnitTestabilityTests.cs index 28f5a2450c..6307b5061b 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerUnitTestabilityTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerUnitTestabilityTests.cs @@ -10,6 +10,7 @@ using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Routing; using Microsoft.AspNet.WebUtilities; using Moq; +using Newtonsoft.Json; using Xunit; namespace Microsoft.AspNet.Mvc @@ -194,7 +195,7 @@ namespace Microsoft.AspNet.Mvc var jsonResult = Assert.IsType(result); Assert.NotNull(jsonResult.Value); Assert.Same(model, jsonResult.Value); - Assert.IsType(model.GetType(), jsonResult.Value); + Assert.IsType(jsonResult.Value); // Arrange controller = new TestabilityController(); @@ -209,6 +210,28 @@ namespace Microsoft.AspNet.Mvc Assert.Null(jsonResult.Value); } + [Fact] + public void ControllerJsonWithSerializerSettings_InvokedInUnitTests() + { + // Arrange + var controller = new TestabilityController(); + var model = new MyModel() { Property1 = "Property_1" }; + var serializerSettings = new JsonSerializerSettings(); + + // Act + var result = controller.JsonWithSerializerSettings_Action(model, serializerSettings); + + // Assert + Assert.NotNull(result); + + var jsonResult = Assert.IsType(result); + Assert.NotNull(jsonResult.Value); + Assert.Same(model, jsonResult.Value); + Assert.IsType(jsonResult.Value); + var jsonFormatter = jsonResult.Formatter as JsonOutputFormatter; + Assert.Same(serializerSettings, jsonFormatter.SerializerSettings); + } + [Fact] public void ControllerHttpNotFound_InvokedInUnitTests() { @@ -590,6 +613,11 @@ namespace Microsoft.AspNet.Mvc return Json(data); } + public IActionResult JsonWithSerializerSettings_Action(object data, JsonSerializerSettings serializerSettings) + { + return Json(data, serializerSettings); + } + public IActionResult Redirect_Action(string url) { return Redirect(url); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/JsonResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/JsonResultTest.cs index 9fa37a02bb..d914d1d9a2 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/JsonResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/JsonResultTest.cs @@ -15,6 +15,7 @@ using Microsoft.Framework.Logging; using Microsoft.Framework.OptionsModel; using Microsoft.Net.Http.Headers; using Moq; +using Newtonsoft.Json; using Xunit; namespace Microsoft.AspNet.Mvc @@ -24,6 +25,9 @@ namespace Microsoft.AspNet.Mvc private static readonly byte[] _abcdUTF8Bytes = new byte[] { 123, 34, 102, 111, 111, 34, 58, 34, 97, 98, 99, 100, 34, 125 }; + private static readonly byte[] _abcdIndentedUTF8Bytes + = new byte[] { 123, 13, 10, 32, 32, 34, 102, 111, 111, 34, 58, 32, 34, 97, 98, 99, 100, 34, 13, 10, 125 }; + [Fact] public async Task ExecuteResultAsync_OptionsFormatter_WithoutBOM() { @@ -154,6 +158,29 @@ namespace Microsoft.AspNet.Mvc Assert.Equal("application/hal+json; charset=utf-8", context.Response.ContentType); } + [Fact] + public async Task ExecuteResultAsync_UsesPassedInSerializerSettings() + { + // Arrange + var expected = _abcdIndentedUTF8Bytes; + + var context = GetHttpContext(); + var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor()); + + var serializerSettings = new JsonSerializerSettings(); + serializerSettings.Formatting = Formatting.Indented; + + var result = new JsonResult(new { foo = "abcd" }, serializerSettings); + + // Act + await result.ExecuteResultAsync(actionContext); + var written = GetWrittenBytes(context); + + // Assert + Assert.Equal(expected, written); + Assert.Equal("application/json; charset=utf-8", context.Response.ContentType); + } + // If no formatter in options can match the given content-types, then use the one registered // in services [Fact] diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonResultTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonResultTest.cs index 6b0577d5f9..75526dfd40 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonResultTest.cs @@ -161,6 +161,26 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal("{\"message\":\"hello\"}", content); } + [Fact] + public async Task JsonResult_Uses_CustomSerializerSettings() + { + // Arrange + var server = TestHelper.CreateServer(_app, SiteName, _configureServices); + var client = server.CreateClient(); + + var url = "http://localhost/JsonResult/CustomSerializerSettings"; + + var request = new HttpRequestMessage(HttpMethod.Get, url); + + // Act + var response = await client.SendAsync(request); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("{\"message\":\"hello\"}", content); + } + [Fact] public async Task JsonResult_CustomContentType() { diff --git a/test/WebSites/BasicWebSite/Controllers/JsonResultController.cs b/test/WebSites/BasicWebSite/Controllers/JsonResultController.cs index c31a0e653e..1772a24aa3 100644 --- a/test/WebSites/BasicWebSite/Controllers/JsonResultController.cs +++ b/test/WebSites/BasicWebSite/Controllers/JsonResultController.cs @@ -3,12 +3,15 @@ using Microsoft.AspNet.Mvc; using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace BasicWebSite.Controllers { public class JsonResultController : Controller { + private static JsonSerializerSettings _customSerializerSettings; + public JsonResult Plain() { return Json(new { Message = "hello" }); @@ -32,6 +35,19 @@ namespace BasicWebSite.Controllers return result; } + public JsonResult CustomSerializerSettings() + { + if (_customSerializerSettings == null) + { + _customSerializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + } + + return Json(new { Message = "hello" }, _customSerializerSettings); + } + public JsonResult Null() { return Json(null);