[Fixes #2715] Removed formatter dependency from JsonResult and JsonViewComponentResult

This commit is contained in:
Ajay Bhargav Baaskaran 2015-06-24 16:23:38 -07:00
parent d2908e7b7b
commit e2787b3b84
12 changed files with 75 additions and 533 deletions

View File

@ -1,13 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.Framework.Internal;
using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
@ -18,13 +15,11 @@ namespace Microsoft.AspNet.Mvc
/// </summary>
public class JsonResult : ActionResult
{
/// <summary>
/// The list of content-types used for formatting when <see cref="ContentTypes"/> is null or empty.
/// </summary>
public static readonly IReadOnlyList<MediaTypeHeaderValue> DefaultContentTypes = new MediaTypeHeaderValue[]
private readonly JsonSerializerSettings _serializerSettings;
private static readonly MediaTypeHeaderValue DefaultContentType = new MediaTypeHeaderValue("application/json")
{
MediaTypeHeaderValue.Parse("application/json"),
MediaTypeHeaderValue.Parse("text/json"),
Encoding = Encoding.UTF8
};
/// <summary>
@ -32,7 +27,7 @@ namespace Microsoft.AspNet.Mvc
/// </summary>
/// <param name="value">The value to format as JSON.</param>
public JsonResult(object value)
: this(value, formatter: null)
: this(value, serializerSettings: SerializerSettingsProvider.CreateSerializerSettings())
{
}
@ -43,32 +38,15 @@ namespace Microsoft.AspNet.Mvc
/// <param name="serializerSettings">The <see cref="JsonSerializerSettings"/> to be used by
/// the formatter.</param>
public JsonResult(object value, [NotNull] JsonSerializerSettings serializerSettings)
: this(value, formatter: new JsonOutputFormatter(serializerSettings))
{
}
/// <summary>
/// Creates a new <see cref="JsonResult"/> with the given <paramref name="value"/>.
/// </summary>
/// <param name="value">The value to format as JSON.</param>
/// <param name="formatter">The formatter to use, or <c>null</c> to choose a formatter dynamically.</param>
public JsonResult(object value, IOutputFormatter formatter)
{
Value = value;
Formatter = formatter;
ContentTypes = new List<MediaTypeHeaderValue>();
_serializerSettings = serializerSettings;
}
/// <summary>
/// Gets or sets the list of supported Content-Types.
/// Gets or sets the <see cref="MediaTypeHeaderValue"/> representing the Content-Type header of the response.
/// </summary>
public IList<MediaTypeHeaderValue> ContentTypes { get; set; }
/// <summary>
/// Gets or sets the formatter.
/// </summary>
public IOutputFormatter Formatter { get; set; }
public MediaTypeHeaderValue ContentType { get; set; }
/// <summary>
/// Gets or sets the HTTP status code.
@ -81,80 +59,44 @@ namespace Microsoft.AspNet.Mvc
public object Value { get; set; }
/// <inheritdoc />
public override async Task ExecuteResultAsync([NotNull] ActionContext context)
public override Task ExecuteResultAsync([NotNull] ActionContext context)
{
var objectResult = new ObjectResult(Value);
var response = context.HttpContext.Response;
// Set the content type explicitly to application/json and text/json.
// if the user has not already set it.
if (ContentTypes == null || ContentTypes.Count == 0)
var contentTypeHeader = ContentType;
if (contentTypeHeader == null)
{
foreach (var contentType in DefaultContentTypes)
{
objectResult.ContentTypes.Add(contentType);
}
contentTypeHeader = DefaultContentType;
}
else
{
objectResult.ContentTypes = ContentTypes;
if (contentTypeHeader.Encoding == null)
{
// 1. Do not modify the user supplied content type
// 2. Parse here to handle parameters apart from charset
contentTypeHeader = MediaTypeHeaderValue.Parse(contentTypeHeader.ToString());
contentTypeHeader.Encoding = Encoding.UTF8;
}
}
var formatterContext = new OutputFormatterContext()
{
HttpContext = context.HttpContext,
DeclaredType = objectResult.DeclaredType,
Object = Value,
};
// JsonResult expects to always find a formatter, in contrast with ObjectResult, which might return
// a 406.
var formatter = SelectFormatter(objectResult, formatterContext);
Debug.Assert(formatter != null);
response.ContentType = contentTypeHeader.ToString();
if (StatusCode != null)
{
context.HttpContext.Response.StatusCode = StatusCode.Value;
response.StatusCode = StatusCode.Value;
}
await formatter.WriteAsync(formatterContext);
}
private IOutputFormatter SelectFormatter(ObjectResult objectResult, OutputFormatterContext formatterContext)
{
if (Formatter == null)
using (var writer = new HttpResponseStreamWriter(response.Body, contentTypeHeader.Encoding))
{
// If no formatter was provided, then run Conneg with the formatters configured in options.
var formatters = formatterContext
.HttpContext
.RequestServices
.GetRequiredService<IOptions<MvcOptions>>()
.Options
.OutputFormatters
.OfType<JsonOutputFormatter>()
.ToArray();
var formatter = objectResult.SelectFormatter(formatterContext, formatters);
if (formatter == null)
using (var jsonWriter = new JsonTextWriter(writer))
{
// If the available user-configured formatters can't write this type, then fall back to the
// 'global' one.
formatter = formatterContext
.HttpContext
.RequestServices
.GetRequiredService<JsonOutputFormatter>();
// Run SelectFormatter again to try to choose a content type that this formatter can do.
objectResult.SelectFormatter(formatterContext, new[] { formatter });
jsonWriter.CloseOutput = false;
var jsonSerializer = JsonSerializer.Create(_serializerSettings);
jsonSerializer.Serialize(jsonWriter, Value);
}
}
return formatter;
}
else
{
// Run SelectFormatter to try to choose a content type that this formatter can do.
objectResult.SelectFormatter(formatterContext, new[] { Formatter });
return Formatter;
}
return Task.FromResult(true);
}
}
}

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.Framework.Internal;
using Newtonsoft.Json;
@ -13,12 +13,14 @@ namespace Microsoft.AspNet.Mvc
/// </summary>
public class JsonViewComponentResult : IViewComponentResult
{
private readonly JsonSerializerSettings _serializerSettings;
/// <summary>
/// Initializes a new <see cref="JsonViewComponentResult"/>.
/// </summary>
/// <param name="value">The value to format as JSON text.</param>
public JsonViewComponentResult(object value)
: this(value, formatter: null)
: this(value, serializerSettings: SerializerSettingsProvider.CreateSerializerSettings())
{
}
@ -29,19 +31,9 @@ namespace Microsoft.AspNet.Mvc
/// <param name="serializerSettings">The <see cref="JsonSerializerSettings"/> to be used by
/// the formatter.</param>
public JsonViewComponentResult(object value, [NotNull] JsonSerializerSettings serializerSettings)
: this(value, new JsonOutputFormatter(serializerSettings))
{
}
/// <summary>
/// Initializes a new <see cref="JsonViewComponentResult"/>.
/// </summary>
/// <param name="value">The value to format as JSON text.</param>
/// <param name="formatter">The <see cref="JsonOutputFormatter"/> to use.</param>
public JsonViewComponentResult(object value, JsonOutputFormatter formatter)
{
Value = value;
Formatter = formatter;
_serializerSettings = serializerSettings;
}
/// <summary>
@ -49,19 +41,18 @@ namespace Microsoft.AspNet.Mvc
/// </summary>
public object Value { get; }
/// <summary>
/// Gets the formatter.
/// </summary>
public JsonOutputFormatter Formatter { get; }
/// <summary>
/// Renders JSON text to the output.
/// </summary>
/// <param name="context">The <see cref="ViewComponentContext"/>.</param>
public void Execute([NotNull] ViewComponentContext context)
{
var formatter = Formatter ?? ResolveFormatter(context);
formatter.WriteObject(context.Writer, Value);
using (var jsonWriter = new JsonTextWriter(context.Writer))
{
jsonWriter.CloseOutput = false;
var jsonSerializer = JsonSerializer.Create(_serializerSettings);
jsonSerializer.Serialize(jsonWriter, Value);
}
}
/// <summary>
@ -74,11 +65,5 @@ namespace Microsoft.AspNet.Mvc
Execute(context);
return Task.FromResult(true);
}
private static JsonOutputFormatter ResolveFormatter(ViewComponentContext context)
{
var services = context.ViewContext.HttpContext.RequestServices;
return services.GetRequiredService<JsonOutputFormatter>();
}
}
}

View File

@ -11,6 +11,7 @@ using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.WebApiCompatShim;
using Microsoft.Framework.Internal;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
namespace System.Web.Http
@ -257,9 +258,7 @@ namespace System.Web.Http
[NonAction]
public virtual JsonResult Json<T>([NotNull] T content, [NotNull] JsonSerializerSettings serializerSettings)
{
var formatter = new JsonOutputFormatter(serializerSettings);
return new JsonResult(content, formatter);
return new JsonResult(content, serializerSettings);
}
/// <summary>
@ -276,12 +275,13 @@ namespace System.Web.Http
[NotNull] JsonSerializerSettings serializerSettings,
[NotNull] Encoding encoding)
{
var formatter = new JsonOutputFormatter(serializerSettings);
var result = new JsonResult(content, serializerSettings);
result.ContentType = new MediaTypeHeaderValue("application/json")
{
Encoding = encoding
};
formatter.SupportedEncodings.Clear();
formatter.SupportedEncodings.Add(encoding);
return new JsonResult(content, formatter);
return result;
}
/// <summary>

View File

@ -1033,7 +1033,7 @@ namespace Microsoft.AspNet.Mvc.Test
}
[Fact]
public void Controller_Json_WithParameterValueAndSerializerSettings_SetsRespectiveValues()
public void Controller_Json_WithParameterValue_SetsRespectiveProperty()
{
// Arrange
var controller = new TestableController();
@ -1046,8 +1046,6 @@ namespace Microsoft.AspNet.Mvc.Test
// Assert
Assert.IsType<JsonResult>(actualJsonResult);
Assert.Same(data, actualJsonResult.Value);
var jsonFormatter = actualJsonResult.Formatter as JsonOutputFormatter;
Assert.Same(serializerSettings, jsonFormatter.SerializerSettings);
}
[Fact]
@ -1094,8 +1092,6 @@ namespace Microsoft.AspNet.Mvc.Test
// Assert
Assert.IsType<JsonResult>(result);
Assert.Same(input, result.Value);
var jsonFormatter = result.Formatter as JsonOutputFormatter;
Assert.Same(serializerSettings, jsonFormatter.SerializerSettings);
mockHttpContext.Verify(
x => x.Response.OnResponseCompleted(It.IsAny<Action<object>>(), It.IsAny<object>()),
Times.Once());

View File

@ -228,8 +228,6 @@ namespace Microsoft.AspNet.Mvc
Assert.NotNull(jsonResult.Value);
Assert.Same(model, jsonResult.Value);
Assert.IsType<MyModel>(jsonResult.Value);
var jsonFormatter = jsonResult.Formatter as JsonOutputFormatter;
Assert.Same(serializerSettings, jsonFormatter.SerializerSettings);
}
[Fact]

View File

@ -1,19 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers;
using Moq;
using Newtonsoft.Json;
using Xunit;
@ -28,18 +22,12 @@ namespace Microsoft.AspNet.Mvc
= 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()
public async Task ExecuteResultAsync_UsesDefaultContentType_IfNoContentTypeSpecified()
{
// Arrange
var expected = _abcdUTF8Bytes;
var optionsFormatters = new List<IOutputFormatter>()
{
Mock.Of<IOutputFormatter>(), // This won't be used
new JsonOutputFormatter(),
};
var context = GetHttpContext(optionsFormatters);
var context = GetHttpContext();
var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());
var result = new JsonResult(new { foo = "abcd" });
@ -54,49 +42,16 @@ namespace Microsoft.AspNet.Mvc
}
[Fact]
public async Task ExecuteResultAsync_Null()
public async Task ExecuteResultAsync_NullEncoding_SetsContentTypeAndDefaultEncoding()
{
// Arrange
var expected = _abcdUTF8Bytes;
var optionsFormatters = new List<IOutputFormatter>()
{
Mock.Of<IOutputFormatter>(), // This won't be used
new JsonOutputFormatter(),
};
var context = GetHttpContext(optionsFormatters);
var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());
var result = new JsonResult(new { foo = "abcd" });
// Act
await result.ExecuteResultAsync(actionContext);
var written = GetWrittenBytes(context);
// Assert
Assert.Equal(expected, written);
Assert.Equal("application/json; charset=utf-8", context.Response.ContentType);
}
[Fact]
public async Task ExecuteResultAsync_OptionsFormatter_Conneg()
{
// Arrange
var expected = _abcdUTF8Bytes;
var optionsFormatters = new List<IOutputFormatter>()
{
Mock.Of<IOutputFormatter>(), // This won't be used
new JsonOutputFormatter(),
};
var context = GetHttpContext(optionsFormatters);
context.Request.Headers["Accept"] = "text/json";
var context = GetHttpContext();
var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());
var result = new JsonResult(new { foo = "abcd" });
result.ContentType = new MediaTypeHeaderValue("text/json");
// Act
await result.ExecuteResultAsync(actionContext);
@ -108,7 +63,7 @@ namespace Microsoft.AspNet.Mvc
}
[Fact]
public async Task ExecuteResultAsync_UsesPassedInFormatter()
public async Task ExecuteResultAsync_SetsContentTypeAndEncoding()
{
// Arrange
var expected = _abcdUTF8Bytes;
@ -116,13 +71,11 @@ namespace Microsoft.AspNet.Mvc
var context = GetHttpContext();
var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());
var formatter = new JsonOutputFormatter();
formatter.SupportedEncodings.Clear();
// This is UTF-8 WITH BOM
formatter.SupportedEncodings.Add(Encoding.UTF8);
var result = new JsonResult(new { foo = "abcd" }, formatter);
var result = new JsonResult(new { foo = "abcd" });
result.ContentType = new MediaTypeHeaderValue("text/json")
{
Encoding = Encoding.ASCII
};
// Act
await result.ExecuteResultAsync(actionContext);
@ -130,31 +83,7 @@ namespace Microsoft.AspNet.Mvc
// Assert
Assert.Equal(expected, written);
Assert.Equal("application/json; charset=utf-8", context.Response.ContentType);
}
[Fact]
public async Task ExecuteResultAsync_UsesPassedInFormatter_ContentTypeSpecified()
{
// Arrange
var expected = _abcdUTF8Bytes;
var context = GetHttpContext();
var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());
var formatter = new JsonOutputFormatter();
formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/hal+json"));
var result = new JsonResult(new { foo = "abcd" }, formatter);
result.ContentTypes.Add(MediaTypeHeaderValue.Parse("application/hal+json"));
// Act
await result.ExecuteResultAsync(actionContext);
var written = GetWrittenBytes(context);
// Assert
Assert.Equal(expected, written);
Assert.Equal("application/hal+json; charset=utf-8", context.Response.ContentType);
Assert.Equal("text/json; charset=us-ascii", context.Response.ContentType);
}
[Fact]
@ -180,67 +109,11 @@ namespace Microsoft.AspNet.Mvc
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]
public async Task ExecuteResultAsync_UsesGlobalFormatter_IfNoFormatterIsConfigured()
{
// Arrange
var expected = _abcdUTF8Bytes;
var context = GetHttpContext(enableFallback: true);
var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());
var result = new JsonResult(new { foo = "abcd" });
// Act
await result.ExecuteResultAsync(actionContext);
var written = GetWrittenBytes(context);
// Assert
Assert.Equal(expected, written);
Assert.Equal("application/json; charset=utf-8", context.Response.ContentType);
}
private HttpContext GetHttpContext(
IReadOnlyList<IOutputFormatter> outputFormatters = null,
bool enableFallback = false)
private HttpContext GetHttpContext()
{
var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();
var services = new Mock<IServiceProvider>(MockBehavior.Strict);
httpContext.RequestServices = services.Object;
var options = new MvcOptions();
if (outputFormatters != null)
{
foreach (var formatter in outputFormatters)
{
options.OutputFormatters.Add(formatter);
}
}
var optionsAccessor = new Mock<IOptions<MvcOptions>>();
optionsAccessor.SetupGet(o => o.Options)
.Returns(options);
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object);
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object);
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
// This is the ultimate fallback, it will be used if none of the formatters from options
// work.
if (enableFallback)
{
services
.Setup(s => s.GetService(typeof(JsonOutputFormatter)))
.Returns(new JsonOutputFormatter());
}
return httpContext;
}

View File

@ -1,14 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Text;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Newtonsoft.Json;
using Xunit;
@ -17,26 +16,6 @@ namespace Microsoft.AspNet.Mvc
{
public class JsonViewComponentResultTest
{
[Fact]
public void Execute_SerializesData_UsingSpecifiedFormatter()
{
// Arrange
var view = Mock.Of<IView>();
var buffer = new MemoryStream();
var viewComponentContext = GetViewComponentContext(view, buffer);
var expectedFormatter = new JsonOutputFormatter();
var result = new JsonViewComponentResult(1, expectedFormatter);
// Act
result.Execute(viewComponentContext);
buffer.Position = 0;
// Assert
Assert.Equal(expectedFormatter, result.Formatter);
Assert.Equal("1", new StreamReader(buffer).ReadToEnd());
}
[Fact]
public void Execute_UsesFormatter_WithSpecifiedSerializerSettings()
{
@ -48,65 +27,14 @@ namespace Microsoft.AspNet.Mvc
var serializerSettings = new JsonSerializerSettings();
serializerSettings.Formatting = Formatting.Indented;
var result = new JsonViewComponentResult("abc", serializerSettings);
var result = new JsonViewComponentResult(new { foo = "abcd" }, serializerSettings);
viewComponentContext.ViewContext.HttpContext.Response.Body = buffer;
// Act
result.Execute(viewComponentContext);
// Assert
Assert.Same(serializerSettings, result.Formatter.SerializerSettings);
}
[Fact]
public void Execute_FallsbackToServices_WhenNoJsonFormatterIsProvided()
{
// Arrange
var view = Mock.Of<IView>();
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider
.Setup(p => p.GetService(typeof(JsonOutputFormatter)))
.Returns(new JsonOutputFormatter())
.Verifiable();
var buffer = new MemoryStream();
var result = new JsonViewComponentResult(1);
var viewComponentContext = GetViewComponentContext(view, buffer);
viewComponentContext.ViewContext.HttpContext.RequestServices = serviceProvider.Object;
// Act
result.Execute(viewComponentContext);
buffer.Position = 0;
// Assert
Assert.Equal("1", new StreamReader(buffer).ReadToEnd());
serviceProvider.Verify();
}
[Fact]
public void Execute_Throws_IfNoFormatterCanBeResolved()
{
// Arrange
var expected = "No service for type 'Microsoft.AspNet.Mvc.JsonOutputFormatter'" +
" has been registered.";
var view = Mock.Of<IView>();
var serviceProvider = new ServiceCollection().BuildServiceProvider();
var buffer = new MemoryStream();
var result = new JsonViewComponentResult(1);
var viewComponentContext = GetViewComponentContext(view, buffer);
viewComponentContext.ViewContext.HttpContext.RequestServices = serviceProvider;
// Act
var ex = Assert.Throws<InvalidOperationException>(() => result.Execute(viewComponentContext));
// Assert
Assert.Equal(expected, ex.Message);
Assert.Equal("{\r\n \"foo\": \"abcd\"\r\n}", Encoding.UTF8.GetString(buffer.ToArray()));
}
private static ViewComponentContext GetViewComponentContext(IView view, Stream stream)

View File

@ -319,80 +319,6 @@ END:VCARD
Assert.Equal(expectedBody, body);
}
[Fact]
public async Task JsonResult_UsesDefaultContentTypes_IfNoneAreAddedExplicitly()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8");
var expectedBody = "{\"MethodName\":\"ReturnJsonResult\"}";
// Act
var response = await client.GetAsync("http://localhost/JsonResult/ReturnJsonResult");
// Assert
Assert.Equal(expectedContentType, response.Content.Headers.ContentType);
var body = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedBody, body);
}
[Fact]
public async Task JsonResult_UsesExplicitContentTypeAndFormatter_IfAdded()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var expectedContentType = MediaTypeHeaderValue.Parse("application/custom-json;charset=utf-8");
var expectedBody = "{ MethodName = ReturnJsonResult_WithCustomMediaType }";
// Act
var response = await client.GetAsync("http://localhost/JsonResult/ReturnJsonResult_WithCustomMediaType");
// Assert
Assert.Equal(expectedContentType, response.Content.Headers.ContentType);
var body = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedBody, body);
}
[Fact]
public async Task JsonResult_UsesDefaultJsonFormatter_IfNoMatchingFormatterIsFound()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8");
var expectedBody = "{\"MethodName\":\"ReturnJsonResult_WithCustomMediaType_NoFormatter\"}";
// Act
var response = await client.GetAsync("http://localhost/JsonResult/ReturnJsonResult_WithCustomMediaType_NoFormatter");
// Assert
Assert.Equal(expectedContentType, response.Content.Headers.ContentType);
var body = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedBody, body);
}
[Fact]
public async Task JsonFormatter_SupportedMediaType_DoesNotChangeAcrossRequests()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8");
var expectedBody = "{\"MethodName\":\"ReturnJsonResult\"}";
for (int i = 0; i < 5; i++)
{
// Act and Assert
var response = await client.GetAsync("http://localhost/JsonResult/ReturnJsonResult");
Assert.Equal(expectedContentType, response.Content.Headers.ContentType);
var body = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedBody, body);
}
}
[Fact]
public async Task XmlFormatter_SupportedMediaType_DoesNotChangeAcrossRequests()
{

View File

@ -17,10 +17,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
private readonly Action<IApplicationBuilder> _app = new BasicWebSite.Startup().Configure;
private readonly Action<IServiceCollection> _configureServices = new BasicWebSite.Startup().ConfigureServices;
[Theory]
[InlineData("application/json")]
[InlineData("text/json")]
public async Task JsonResult_Conneg(string mediaType)
[Fact]
public async Task JsonResult_UsesDefaultContentType()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
@ -29,7 +27,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var url = "http://localhost/JsonResult/Plain";
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.TryAddWithoutValidation("Accept", mediaType);
// Act
var response = await client.SendAsync(request);
@ -37,7 +34,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(mediaType, response.Content.Headers.ContentType.MediaType);
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
Assert.Equal("{\"Message\":\"hello\"}", content);
}
@ -111,56 +108,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal("\"hello\"", content);
}
[Theory]
[InlineData("application/json")]
[InlineData("text/json")]
public async Task JsonResult_CustomFormatter_Conneg(string mediaType)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var url = "http://localhost/JsonResult/CustomFormatter";
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);
}
// Using an Accept header can't force Json to not be Json. If your accept header doesn't jive with the
// formatters/content-type configured on the result it will be ignored.
[Theory]
[InlineData("application/xml")]
[InlineData("text/xml")]
public async Task JsonResult_CustomFormatter_Conneg_Fails(string mediaType)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var url = "http://localhost/JsonResult/CustomFormatter";
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("application/json", response.Content.Headers.ContentType.MediaType);
Assert.Equal("{\"message\":\"hello\"}", content);
}
[Fact]
public async Task JsonResult_Uses_CustomSerializerSettings()
{

View File

@ -250,26 +250,7 @@ namespace System.Web.Http
}
[Fact]
public void ApiController_Json_Settings()
{
// Arrange
var controller = new ConcreteApiController();
var product = new Product();
var settings = new JsonSerializerSettings();
// Act
var result = controller.Json(product, settings);
// Assert
var jsonResult = Assert.IsType<JsonResult>(result);
Assert.Same(product, jsonResult.Value);
var formatter = Assert.IsType<JsonOutputFormatter>(jsonResult.Formatter);
Assert.Same(settings, formatter.SerializerSettings);
}
[Fact]
public void ApiController_Json_Settings_Encoding()
public void ApiController_Json_Encoding()
{
// Arrange
var controller = new ConcreteApiController();
@ -283,9 +264,7 @@ namespace System.Web.Http
var jsonResult = Assert.IsType<JsonResult>(result);
Assert.Same(product, jsonResult.Value);
var formatter = Assert.IsType<JsonOutputFormatter>(jsonResult.Formatter);
Assert.Same(settings, formatter.SerializerSettings);
Assert.Same(Encoding.UTF8, Assert.Single(formatter.SupportedEncodings));
Assert.Same(Encoding.UTF8, jsonResult.ContentType.Encoding);
}
[Fact]

View File

@ -17,21 +17,10 @@ namespace BasicWebSite.Controllers
return Json(new { Message = "hello" });
}
public JsonResult CustomFormatter()
{
var formatter = new JsonOutputFormatter();
formatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
return new JsonResult(new { Message = "hello" }, formatter);
}
public JsonResult CustomContentType()
{
var formatter = new JsonOutputFormatter();
formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/message+json"));
var result = new JsonResult(new { Message = "hello" }, formatter);
result.ContentTypes.Add(MediaTypeHeaderValue.Parse("application/message+json"));
var result = new JsonResult(new { Message = "hello" });
result.ContentType = MediaTypeHeaderValue.Parse("application/message+json");
return result;
}

View File

@ -2,32 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
using Microsoft.Net.Http.Headers;
namespace ContentNegotiationWebSite
{
public class JsonResultController : Controller
{
public IActionResult ReturnJsonResult()
{
return new JsonResult(new { MethodName = "ReturnJsonResult" });
}
public IActionResult ReturnJsonResult_WithCustomMediaType()
{
var jsonResult = new JsonResult(new { MethodName = "ReturnJsonResult_WithCustomMediaType" },
new CustomFormatter("application/custom-json"));
jsonResult.ContentTypes.Add(MediaTypeHeaderValue.Parse("application/custom-json"));
return jsonResult;
}
public IActionResult ReturnJsonResult_WithCustomMediaType_NoFormatter()
{
var jsonResult = new JsonResult(new { MethodName = "ReturnJsonResult_WithCustomMediaType_NoFormatter" });
jsonResult.ContentTypes.Add(MediaTypeHeaderValue.Parse("application/custom-json"));
return jsonResult;
}
[Produces("application/xml")]
public IActionResult Produces_WithNonObjectResult()
{