diff --git a/src/Microsoft.AspNet.Mvc.Core/ContentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ContentResult.cs index d2bbf4e023..6af9614891 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ContentResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ContentResult.cs @@ -47,32 +47,31 @@ namespace Microsoft.AspNet.Mvc var logger = loggerFactory.CreateLogger(); var response = context.HttpContext.Response; - var contentTypeHeader = ContentType; - if (contentTypeHeader != null && contentTypeHeader.Encoding == null) - { - // Do not modify the user supplied content type, so copy it instead - contentTypeHeader = contentTypeHeader.Copy(); - contentTypeHeader.Encoding = Encoding.UTF8; - } + string resolvedContentType; + Encoding resolvedContentTypeEncoding; + ResponseContentTypeHelper.ResolveContentTypeAndEncoding( + ContentType, + response.ContentType, + DefaultContentType, + out resolvedContentType, + out resolvedContentTypeEncoding); - response.ContentType = contentTypeHeader?.ToString() - ?? response.ContentType - ?? DefaultContentType.ToString(); + response.ContentType = resolvedContentType; if (StatusCode != null) { response.StatusCode = StatusCode.Value; } - logger.ContentResultExecuting(contentTypeHeader); + logger.ContentResultExecuting(resolvedContentType); if (Content != null) { var bufferingFeature = response.HttpContext.Features.Get(); bufferingFeature?.DisableResponseBuffering(); - return response.WriteAsync(Content, contentTypeHeader?.Encoding ?? DefaultContentType.Encoding); + return response.WriteAsync(Content, resolvedContentTypeEncoding); } return TaskCache.CompletedTask; diff --git a/src/Microsoft.AspNet.Mvc.Core/Internal/ResponseContentTypeHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Internal/ResponseContentTypeHelper.cs new file mode 100644 index 0000000000..b2397a8030 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Internal/ResponseContentTypeHelper.cs @@ -0,0 +1,73 @@ +// 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.Diagnostics; +using System.Text; +using Microsoft.Net.Http.Headers; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Mvc.Internal +{ + public static class ResponseContentTypeHelper + { + /// + /// Gets the content type and encoding that need to be used for the response. + /// The priority for selecting the content type is: + /// 1. ContentType property set on the action result + /// 2. property set on + /// 3. Default content type set on the action result + /// + /// + /// The user supplied content type is not modified and is used as is. For example, if user + /// sets the content type to be "text/plain" without any encoding, then the default content type's + /// encoding is used to write the response and the ContentType header is set to be "text/plain" without any + /// "charset" information. + /// + /// ContentType set on the action result + /// property set + /// on + /// The default content type of the action result. + /// The content type to be used for the response content type header + /// Encoding to be used for writing the response + public static void ResolveContentTypeAndEncoding( + MediaTypeHeaderValue actionResultContentType, + string httpResponseContentType, + MediaTypeHeaderValue defaultContentType, + out string resolvedContentType, + out Encoding resolvedContentTypeEncoding) + { + Debug.Assert(defaultContentType != null); + Debug.Assert(defaultContentType.Encoding != null); + + // 1. User sets the ContentType property on the action result + if (actionResultContentType != null) + { + resolvedContentType = actionResultContentType.ToString(); + resolvedContentTypeEncoding = actionResultContentType.Encoding ?? defaultContentType.Encoding; + return; + } + + // 2. User sets the ContentType property on the http response directly + if (!string.IsNullOrEmpty(httpResponseContentType)) + { + MediaTypeHeaderValue mediaType; + if (MediaTypeHeaderValue.TryParse(httpResponseContentType, out mediaType)) + { + resolvedContentType = httpResponseContentType; + resolvedContentTypeEncoding = mediaType.Encoding ?? defaultContentType.Encoding; + } + else + { + resolvedContentType = httpResponseContentType; + resolvedContentTypeEncoding = defaultContentType.Encoding; + } + + return; + } + + // 3. Fall-back to the default content type + resolvedContentType = defaultContentType.ToString(); + resolvedContentTypeEncoding = defaultContentType.Encoding; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ContentResultLoggerExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ContentResultLoggerExtensions.cs index 6bf3afd8b1..759d456225 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Logging/ContentResultLoggerExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ContentResultLoggerExtensions.cs @@ -19,9 +19,9 @@ namespace Microsoft.AspNet.Mvc.Logging "Executing ContentResult with HTTP Response ContentType of {ContentType}"); } - public static void ContentResultExecuting(this ILogger logger, MediaTypeHeaderValue contentType) + public static void ContentResultExecuting(this ILogger logger, string contentType) { - _contentResultExecuting(logger, contentType?.MediaType, null); + _contentResultExecuting(logger, contentType, null); } } } diff --git a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonResult.cs b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonResult.cs index c3bf2030b6..9273b70af3 100644 --- a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonResult.cs +++ b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonResult.cs @@ -4,6 +4,7 @@ using System; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.OptionsModel; @@ -79,22 +80,16 @@ namespace Microsoft.AspNet.Mvc var response = context.HttpContext.Response; - var contentTypeHeader = ContentType; - if (contentTypeHeader == null) - { - contentTypeHeader = DefaultContentType; - } - else - { - if (contentTypeHeader.Encoding == null) - { - // Do not modify the user supplied content type, so copy it instead - contentTypeHeader = contentTypeHeader.Copy(); - contentTypeHeader.Encoding = Encoding.UTF8; - } - } + string resolvedContentType = null; + Encoding resolvedContentTypeEncoding = null; + ResponseContentTypeHelper.ResolveContentTypeAndEncoding( + ContentType, + response.ContentType, + DefaultContentType, + out resolvedContentType, + out resolvedContentTypeEncoding); - response.ContentType = contentTypeHeader.ToString(); + response.ContentType = resolvedContentType; if (StatusCode != null) { @@ -113,8 +108,7 @@ namespace Microsoft.AspNet.Mvc } logger.JsonResultExecuting(Value); - - using (var writer = new HttpResponseStreamWriter(response.Body, contentTypeHeader.Encoding)) + using (var writer = new HttpResponseStreamWriter(response.Body, resolvedContentTypeEncoding)) { using (var jsonWriter = new JsonTextWriter(writer)) { diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs index 372078f107..bb04a44ba0 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs @@ -4,6 +4,7 @@ using System; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.Logging; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.Rendering; @@ -90,32 +91,23 @@ namespace Microsoft.AspNet.Mvc tempData = services.GetRequiredService(); } - var contentType = ContentType; - if (contentType != null && contentType.Encoding == null) - { - // Do not modify the user supplied content type, so copy it instead - contentType = contentType.Copy(); - contentType.Encoding = Encoding.UTF8; - } + string resolvedContentType = null; + Encoding resolvedContentTypeEncoding = null; + ResponseContentTypeHelper.ResolveContentTypeAndEncoding( + ContentType, + response.ContentType, + ViewExecutor.DefaultContentType, + out resolvedContentType, + out resolvedContentTypeEncoding); - // Priority list for setting content-type/encoding: - // 1. this.ContentType (set by the user on the result) - // 2. response.ContentType (likely set by the user in controller code) - // 3. ViewExecutor.DefaultContentType (sensible default) - // - // - response.ContentType = - contentType?.ToString() ?? - response.ContentType ?? - ViewExecutor.DefaultContentType.ToString(); + response.ContentType = resolvedContentType; if (StatusCode != null) { response.StatusCode = StatusCode.Value; } - var encoding = contentType?.Encoding ?? ViewExecutor.DefaultContentType?.Encoding; - using (var writer = new HttpResponseStreamWriter(response.Body, encoding)) + using (var writer = new HttpResponseStreamWriter(response.Body, resolvedContentTypeEncoding)) { var viewContext = new ViewContext( context, diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewExecutor.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewExecutor.cs index d6fa0258e1..dce9403185 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewExecutor.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewExecutor.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.Diagnostics; using Microsoft.AspNet.Mvc.Infrastructure; +using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.ViewEngines; @@ -96,7 +97,8 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures /// The . /// The . /// - /// The content-type header value to set in the response. If null, will be used. + /// The content-type header value to set in the response. If null, + /// will be used. /// /// /// The HTTP status code to set in the response. May be null. @@ -134,26 +136,23 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures var response = actionContext.HttpContext.Response; - if (contentType != null && contentType.Encoding == null) - { - // Do not modify the user supplied content type, so copy it instead - contentType = contentType.Copy(); - contentType.Encoding = Encoding.UTF8; - } + string resolvedContentType = null; + Encoding resolvedContentTypeEncoding = null; + ResponseContentTypeHelper.ResolveContentTypeAndEncoding( + contentType, + response.ContentType, + DefaultContentType, + out resolvedContentType, + out resolvedContentTypeEncoding); - // Priority list for setting content-type: - // 1. passed in contentType (likely set by the user on the result) - // 2. response.ContentType (likely set by the user in controller code) - // 3. ViewExecutor.DefaultContentType (sensible default) - response.ContentType = contentType?.ToString() ?? response.ContentType ?? DefaultContentType.ToString(); + response.ContentType = resolvedContentType; if (statusCode != null) { response.StatusCode = statusCode.Value; } - var encoding = contentType?.Encoding ?? DefaultContentType.Encoding; - using (var writer = WriterFactory.CreateWriter(response.Body, encoding)) + using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding)) { var viewContext = new ViewContext( actionContext, @@ -162,7 +161,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures tempData, writer, ViewOptions.HtmlHelperOptions); - + DiagnosticSource.BeforeView(view, viewContext); await view.RenderAsync(viewContext); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ContentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ContentResultTest.cs index 51f4d7ad9c..8b18280ca5 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ContentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ContentResultTest.cs @@ -22,47 +22,6 @@ namespace Microsoft.AspNet.Mvc { public class ContentResultTest { - [Fact] - public async Task ContentResult_Response_NullEncoding_SetsContentTypeAndDefaultEncoding() - { - // Arrange - var contentResult = new ContentResult - { - Content = "Test Content", - ContentType = new MediaTypeHeaderValue("application/json") - }; - var httpContext = GetHttpContext(); - var actionContext = GetActionContext(httpContext); - - // Act - await contentResult.ExecuteResultAsync(actionContext); - - // Assert - Assert.Equal("application/json; charset=utf-8", httpContext.Response.ContentType); - } - - [Fact] - public async Task ContentResult_Response_SetsContentTypeAndEncoding() - { - // Arrange - var contentResult = new ContentResult - { - Content = "Test Content", - ContentType = new MediaTypeHeaderValue("text/plain") - { - Encoding = Encoding.ASCII - } - }; - var httpContext = GetHttpContext(); - var actionContext = GetActionContext(httpContext); - - // Act - await contentResult.ExecuteResultAsync(actionContext); - - // Assert - Assert.Equal("text/plain; charset=us-ascii", httpContext.Response.ContentType); - } - [Fact] public async Task ContentResult_Response_NullContent_SetsContentTypeAndEncoding() { @@ -118,6 +77,7 @@ namespace Microsoft.AspNet.Mvc { get { + // contentType, content, responseContentType, expectedContentType, expectedData return new TheoryData { { @@ -131,14 +91,14 @@ namespace Microsoft.AspNet.Mvc new MediaTypeHeaderValue("text/foo"), "κόσμε", null, - "text/foo; charset=utf-8", + "text/foo", new byte[] { 206, 186, 225, 189, 185, 207, 131, 206, 188, 206, 181 } //utf-8 without BOM }, { MediaTypeHeaderValue.Parse("text/foo;p1=p1-value"), "κόσμε", null, - "text/foo; p1=p1-value; charset=utf-8", + "text/foo; p1=p1-value", new byte[] { 206, 186, 225, 189, 185, 207, 131, 206, 188, 206, 181 } //utf-8 without BOM }, { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Internal/ResponseContentTypeHelperTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Internal/ResponseContentTypeHelperTest.cs new file mode 100644 index 0000000000..ebfe90f163 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Internal/ResponseContentTypeHelperTest.cs @@ -0,0 +1,143 @@ +// 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.Text; +using Microsoft.Net.Http.Headers; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Internal +{ + public class ResponseContentTypeHelperTest + { + public static TheoryData ResponseContentTypeData + { + get + { + // contentType, responseContentType, expectedContentType + return new TheoryData + { + // No explicit content type is provided, fall-back to the default content type + { + null, + null, + "text/default; p1=p1-value; charset=utf-8" + }, + + // Content type is set explicitly without encoding on action result. No charset parameter added in + // expected content type + { + new MediaTypeHeaderValue("text/foo"), + null, + "text/foo" + }, + + // Content type is set explicitly with encoding on action result. Expected content type + // has the charset + { + MediaTypeHeaderValue.Parse("text/foo; charset=us-ascii"), + null, + "text/foo; charset=us-ascii" + }, + + // Content type is set explicitly without encoding and additional parameters on action result + // Expected content type has the additional parameters but with no charset. + { + MediaTypeHeaderValue.Parse("text/foo; p1=p1-value"), + null, + "text/foo; p1=p1-value" + }, + + // Content type is set explicitly with encoding and additional parameters on action result + // Expected content type has the additional parameters and the charset. + { + MediaTypeHeaderValue.Parse("text/foo; p1=p1-value; charset=us-ascii"), + null, + "text/foo; p1=p1-value; charset=us-ascii" + }, + + // Content type is set explicitly without encoding on http response. + // No charset parameter added in expected content type + { + null, + "text/bar", + "text/bar" + }, + + // Content type is set explicitly without encoding and additional parameters on http response + // No charset parameter added in expected content type + { + null, + "text/bar; p1=p1-value", + "text/bar; p1=p1-value" + }, + + // Content type is set explicitly with encoding and additional parameters on http response + // Expected content type has charset and additional parameters + { + null, + "text/bar; p1=p1-value; charset=us-ascii", + "text/bar; p1=p1-value; charset=us-ascii" + }, + + // Content type set on action result takes precedence over the conten type set on http response + { + MediaTypeHeaderValue.Parse("text/foo; charset=us-ascii"), + "text/bar", + "text/foo; charset=us-ascii" + }, + { + MediaTypeHeaderValue.Parse("text/foo; charset=us-ascii"), + "text/bar; charset=utf-8", + "text/foo; charset=us-ascii" + } + }; + } + } + + [Theory] + [MemberData(nameof(ResponseContentTypeData))] + public void GetsExpectedContentTypeAndEncoding( + MediaTypeHeaderValue contentType, + string responseContentType, + string expectedContentType) + { + // Arrange + var defaultContentType = MediaTypeHeaderValue.Parse("text/default; p1=p1-value; charset=utf-8"); + + // Act + string resolvedContentType = null; + Encoding resolvedContentTypeEncoding = null; + ResponseContentTypeHelper.ResolveContentTypeAndEncoding( + contentType, + responseContentType, + defaultContentType, + out resolvedContentType, + out resolvedContentTypeEncoding); + + // Assert + Assert.Equal(expectedContentType, resolvedContentType); + } + + [Fact] + public void DoesNotThrowException_OnInvalidResponseContentType() + { + // Arrange + var expectedContentType = "invalid-content-type"; + var defaultContentType = MediaTypeHeaderValue.Parse("text/plain; charset=utf-8"); + + // Act + string resolvedContentType = null; + Encoding resolvedContentTypeEncoding = null; + ResponseContentTypeHelper.ResolveContentTypeAndEncoding( + null, + expectedContentType, + defaultContentType, + out resolvedContentType, + out resolvedContentTypeEncoding); + + // Assert + Assert.Equal(expectedContentType, resolvedContentType); + Assert.Equal(Encoding.UTF8, resolvedContentTypeEncoding); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonResultTest.cs b/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonResultTest.cs index 3855d1b32b..946b660a82 100644 --- a/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonResultTest.cs @@ -45,7 +45,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task ExecuteResultAsync_NullEncoding_SetsContentTypeAndDefaultEncoding() + public async Task ExecuteResultAsync_NullEncoding_DoesNotSetCharsetOnContentType() { // Arrange var expected = _abcdUTF8Bytes; @@ -62,7 +62,7 @@ namespace Microsoft.AspNet.Mvc // Assert Assert.Equal(expected, written); - Assert.Equal("text/json; charset=utf-8", context.Response.ContentType); + Assert.Equal("text/json", context.Response.ContentType); } [Fact] @@ -89,6 +89,52 @@ namespace Microsoft.AspNet.Mvc Assert.Equal("text/json; charset=us-ascii", context.Response.ContentType); } + [Fact] + public async Task NoResultContentTypeSet_UsesResponseContentType_AndSuppliedEncoding() + { + // Arrange + var expectedData = Encoding.ASCII.GetBytes("{\"foo\":\"abcd\"}"); + var expectedContentType = "text/foo; p1=p1-value; charset=us-ascii"; + var context = GetHttpContext(); + context.Response.ContentType = expectedContentType; + 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(expectedData, written); + Assert.Equal(expectedContentType, context.Response.ContentType); + } + + [Theory] + [InlineData("text/foo", "text/foo")] + [InlineData("text/foo; p1=p1-value", "text/foo; p1=p1-value")] + public async Task NoResultContentTypeSet_UsesResponseContentTypeAndDefaultEncoding_DoesNotSetCharset( + string responseContentType, + string expectedContentType) + { + // Arrange + var expected = _abcdUTF8Bytes; + + var context = GetHttpContext(); + context.Response.ContentType = responseContentType; + 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(expectedContentType, context.Response.ContentType); + } + private static List AbcdIndentedUTF8Bytes { get diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs index 79edd4808d..336e4bd745 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs @@ -201,7 +201,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests } [Fact] - public async Task ContentResult_WritesContent_SetsContentTypeWithDefaultEncoding() + public async Task ContentResult_WritesContent_SetsContentType_UsesDefaultEncoding_AndNoCharset() { // Arrange var request = new HttpRequestMessage( @@ -214,7 +214,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); - Assert.Equal("utf-8", response.Content.Headers.ContentType.CharSet); Assert.Equal("content", await response.Content.ReadAsStringAsync()); } diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponentResultTest.cs index a79574ecaa..420a458029 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponentResultTest.cs @@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc }; var actionContext = CreateActionContext(descriptor); - + var viewComponentResult = new ViewComponentResult { Arguments = new object[] { "World!" }, @@ -338,11 +338,11 @@ namespace Microsoft.AspNet.Mvc }, { new MediaTypeHeaderValue("text/foo"), - "text/foo; charset=utf-8" + "text/foo" }, { MediaTypeHeaderValue.Parse("text/foo;p1=p1-value"), - "text/foo; p1=p1-value; charset=utf-8" + "text/foo; p1=p1-value" }, { new MediaTypeHeaderValue("text/foo") { Encoding = Encoding.ASCII }, @@ -422,8 +422,13 @@ namespace Microsoft.AspNet.Mvc Assert.Equal(expectedContentType, actionContext.HttpContext.Response.ContentType); } - [Fact] - public async Task ViewComponentResult_NoContentTypeSet_PreservesResponseContentType() + [Theory] + [InlineData("text/foo", "text/foo; charset=utf-8")] + [InlineData("text/foo; p1=p1-value", "text/foo; p1=p1-value; charset=utf-8")] + [InlineData("text/foo; p1=p1-value; charset=us-ascii", "text/foo; p1=p1-value; charset=us-ascii")] + public async Task ViewComponentResult_NoContentTypeSet_PreservesResponseContentType( + string responseContentType, + string expectedContentType) { // Arrange var descriptor = new ViewComponentDescriptor() @@ -435,7 +440,6 @@ namespace Microsoft.AspNet.Mvc var actionContext = CreateActionContext(descriptor); - var expectedContentType = "application/x-will-not-be-overridden"; actionContext.HttpContext.Response.ContentType = expectedContentType; var viewComponentResult = new ViewComponentResult() @@ -453,7 +457,7 @@ namespace Microsoft.AspNet.Mvc } private IServiceCollection CreateServices(object diagnosticListener, HttpContext context, params ViewComponentDescriptor[] descriptors) - { + { var httpContext = new HttpContextAccessor() { HttpContext = context }; var tempDataProvider = new SessionStateTempDataProvider(); var diagnosticSource = new DiagnosticListener("Microsoft.AspNet"); diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/PartialViewResultExecutorTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/PartialViewResultExecutorTest.cs index 694c0466c5..203b81aa27 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/PartialViewResultExecutorTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/PartialViewResultExecutorTest.cs @@ -70,7 +70,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures // Assert Assert.Equal(viewName, viewEngineResult.ViewName); } - + [Fact] public void FindView_WritesDiagnostic_ViewFound() { @@ -163,7 +163,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures await executor.ExecuteAsync(context, Mock.Of(), viewResult); // Assert - Assert.Equal("application/x-my-content-type; charset=utf-8", context.HttpContext.Response.ContentType); + Assert.Equal("application/x-my-content-type", context.HttpContext.Response.ContentType); // Check if the original instance provided by the user has not changed. // Since we do not have access to the new instance created within the view executor, @@ -199,7 +199,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures { return new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); } - + private PartialViewResultExecutor GetViewExecutor(DiagnosticSource diagnosticSource = null) { if (diagnosticSource == null) diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewExecutorTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewExecutorTest.cs index 81ae3de7d6..f389daa559 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewExecutorTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewExecutorTest.cs @@ -38,17 +38,22 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures { new MediaTypeHeaderValue("text/foo"), null, - "text/foo; charset=utf-8" + "text/foo" + }, + { + MediaTypeHeaderValue.Parse("text/foo; charset=us-ascii"), + null, + "text/foo; charset=us-ascii" }, { MediaTypeHeaderValue.Parse("text/foo; p1=p1-value"), null, - "text/foo; p1=p1-value; charset=utf-8" + "text/foo; p1=p1-value" }, { - new MediaTypeHeaderValue("text/foo") { Charset = "us-ascii" }, + MediaTypeHeaderValue.Parse("text/foo; p1=p1-value; charset=us-ascii"), null, - "text/foo; charset=us-ascii" + "text/foo; p1=p1-value; charset=us-ascii" }, { null, @@ -57,19 +62,24 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures }, { null, - "application/xml; charset=us-ascii", - "application/xml; charset=us-ascii" + "text/bar; p1=p1-value", + "text/bar; p1=p1-value" }, - { + { null, - "Invalid content type", - "Invalid content type" + "text/bar; p1=p1-value; charset=us-ascii", + "text/bar; p1=p1-value; charset=us-ascii" }, { - new MediaTypeHeaderValue("text/foo") { Charset = "us-ascii" }, + MediaTypeHeaderValue.Parse("text/foo; charset=us-ascii"), "text/bar", "text/foo; charset=us-ascii" }, + { + MediaTypeHeaderValue.Parse("text/foo; charset=us-ascii"), + "text/bar; charset=utf-8", + "text/foo; charset=us-ascii" + } }; } } @@ -150,7 +160,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures context, new RouteData(), new ActionDescriptor()); - + context.RequestServices = GetServiceProvider(); var viewExecutor = CreateViewExecutor(); diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewResultExecutorTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewResultExecutorTest.cs index 942b88b0c8..a929bc1849 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewResultExecutorTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewResultExecutorTest.cs @@ -163,7 +163,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures await executor.ExecuteAsync(context, Mock.Of(), viewResult); // Assert - Assert.Equal("application/x-my-content-type; charset=utf-8", context.HttpContext.Response.ContentType); + Assert.Equal("application/x-my-content-type", context.HttpContext.Response.ContentType); // Check if the original instance provided by the user has not changed. // Since we do not have access to the new instance created within the view executor, @@ -199,7 +199,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures { return new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); } - + private ViewResultExecutor GetViewExecutor(DiagnosticListener diagnosticSource = null) { if (diagnosticSource == null)