diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs
index ccf9cba086..4592967e02 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.Internal;
using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers;
+using Newtonsoft.Json;
namespace Microsoft.AspNet.Mvc
{
@@ -35,6 +36,16 @@ public JsonResult(object value)
{
}
+ ///
+ /// Creates a new with the given .
+ ///
+ /// The value to format as JSON.
+ /// The to be used by the formatter.
+ public JsonResult(object value, JsonSerializerSettings serializerSettings)
+ : this(value, formatter: new JsonOutputFormatter { SerializerSettings = serializerSettings })
+ {
+ }
+
///
/// Creates a new with the given .
///
@@ -48,6 +59,7 @@ public JsonResult(object value, IOutputFormatter formatter)
ContentTypes = new List();
}
+
///
/// Gets or sets the list of supported Content-Types.
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs
index 2cf4f110da..9fe99c5ae9 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs
@@ -15,6 +15,7 @@
using Microsoft.AspNet.Routing;
using Microsoft.Framework.Internal;
using Microsoft.Net.Http.Headers;
+using Newtonsoft.Json;
namespace Microsoft.AspNet.Mvc
{
@@ -399,6 +400,28 @@ public virtual JsonResult Json(object data)
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
+ /// to JSON format for the response.
+ /// It is recommended to cache the instance for the action
+ /// since it creates a new contract for every instance.
+ [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..9c5b9dff96 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs
@@ -18,6 +18,7 @@
using Microsoft.AspNet.WebUtilities;
#if DNX451
using Moq;
+using Newtonsoft.Json;
#endif
using Xunit;
@@ -1031,6 +1032,24 @@ public void Controller_Json_WithParameterValue_SetsResultData()
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..4b47f515ed 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.Routing;
using Microsoft.AspNet.WebUtilities;
using Moq;
+using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNet.Mvc
@@ -209,6 +210,28 @@ public void ControllerJson_InvokedInUnitTests()
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(model.GetType(), jsonResult.Value);
+ var jsonFormatter = jsonResult.Formatter as JsonOutputFormatter;
+ Assert.Same(serializerSettings, jsonFormatter.SerializerSettings);
+ }
+
[Fact]
public void ControllerHttpNotFound_InvokedInUnitTests()
{
@@ -590,6 +613,11 @@ public IActionResult Json_Action(object data)
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.OptionsModel;
using Microsoft.Net.Http.Headers;
using Moq;
+using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNet.Mvc
@@ -24,6 +25,9 @@ public class JsonResultTest
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 @@ public async Task ExecuteResultAsync_UsesPassedInFormatter_ContentTypeSpecified(
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..483eb1a146 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonResultTest.cs
@@ -161,6 +161,30 @@ public async Task JsonResult_CustomFormatter_Conneg_Fails(string mediaType)
Assert.Equal("{\"message\":\"hello\"}", content);
}
+ [Theory]
+ [InlineData("application/json")]
+ [InlineData("text/json")]
+ public async Task JsonResult_CustomSerializerSettings_Conneg(string mediaType)
+ {
+ // 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);
+ request.Headers.TryAddWithoutValidation("Accept", mediaType);
+
+ // Act
+ var response = await client.SendAsync(request);
+ var content = await response.Content.ReadAsStringAsync();
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(mediaType, response.Content.Headers.ContentType.MediaType);
+ 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..a324f311df 100644
--- a/test/WebSites/BasicWebSite/Controllers/JsonResultController.cs
+++ b/test/WebSites/BasicWebSite/Controllers/JsonResultController.cs
@@ -3,6 +3,7 @@
using Microsoft.AspNet.Mvc;
using Microsoft.Net.Http.Headers;
+using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace BasicWebSite.Controllers
@@ -32,6 +33,14 @@ public JsonResult CustomContentType()
return result;
}
+ public JsonResult CustomSerializerSettings()
+ {
+ var serializerSettings = new JsonSerializerSettings();
+ serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
+
+ return new JsonResult(new { Message = "hello" }, serializerSettings);
+ }
+
public JsonResult Null()
{
return Json(null);