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);