[Fixes #3177] Clean up logic for encoding in ViewResult and friends

This commit is contained in:
Kiran Challa 2015-10-21 12:35:24 -07:00
parent cd761a644d
commit 5364468001
14 changed files with 354 additions and 135 deletions

View File

@ -47,32 +47,31 @@ namespace Microsoft.AspNet.Mvc
var logger = loggerFactory.CreateLogger<ContentResult>(); var logger = loggerFactory.CreateLogger<ContentResult>();
var response = context.HttpContext.Response; var response = context.HttpContext.Response;
var contentTypeHeader = ContentType;
if (contentTypeHeader != null && contentTypeHeader.Encoding == null) string resolvedContentType;
{ Encoding resolvedContentTypeEncoding;
// Do not modify the user supplied content type, so copy it instead ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
contentTypeHeader = contentTypeHeader.Copy(); ContentType,
contentTypeHeader.Encoding = Encoding.UTF8; response.ContentType,
} DefaultContentType,
out resolvedContentType,
out resolvedContentTypeEncoding);
response.ContentType = contentTypeHeader?.ToString() response.ContentType = resolvedContentType;
?? response.ContentType
?? DefaultContentType.ToString();
if (StatusCode != null) if (StatusCode != null)
{ {
response.StatusCode = StatusCode.Value; response.StatusCode = StatusCode.Value;
} }
logger.ContentResultExecuting(contentTypeHeader); logger.ContentResultExecuting(resolvedContentType);
if (Content != null) if (Content != null)
{ {
var bufferingFeature = response.HttpContext.Features.Get<IHttpBufferingFeature>(); var bufferingFeature = response.HttpContext.Features.Get<IHttpBufferingFeature>();
bufferingFeature?.DisableResponseBuffering(); bufferingFeature?.DisableResponseBuffering();
return response.WriteAsync(Content, contentTypeHeader?.Encoding ?? DefaultContentType.Encoding); return response.WriteAsync(Content, resolvedContentTypeEncoding);
} }
return TaskCache.CompletedTask; return TaskCache.CompletedTask;

View File

@ -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
{
/// <summary>
/// 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. <see cref="HttpResponse.ContentType"/> property set on <see cref="HttpResponse"/>
/// 3. Default content type set on the action result
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="actionResultContentType">ContentType set on the action result</param>
/// <param name="httpResponseContentType"><see cref="HttpResponse.ContentType"/> property set
/// on <see cref="HttpResponse"/></param>
/// <param name="defaultContentType">The default content type of the action result.</param>
/// <param name="resolvedContentType">The content type to be used for the response content type header</param>
/// <param name="resolvedContentTypeEncoding">Encoding to be used for writing the response</param>
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;
}
}
}

View File

@ -19,9 +19,9 @@ namespace Microsoft.AspNet.Mvc.Logging
"Executing ContentResult with HTTP Response ContentType of {ContentType}"); "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);
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.OptionsModel; using Microsoft.Extensions.OptionsModel;
@ -79,22 +80,16 @@ namespace Microsoft.AspNet.Mvc
var response = context.HttpContext.Response; var response = context.HttpContext.Response;
var contentTypeHeader = ContentType; string resolvedContentType = null;
if (contentTypeHeader == null) Encoding resolvedContentTypeEncoding = null;
{ ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
contentTypeHeader = DefaultContentType; ContentType,
} response.ContentType,
else DefaultContentType,
{ out resolvedContentType,
if (contentTypeHeader.Encoding == null) out resolvedContentTypeEncoding);
{
// Do not modify the user supplied content type, so copy it instead
contentTypeHeader = contentTypeHeader.Copy();
contentTypeHeader.Encoding = Encoding.UTF8;
}
}
response.ContentType = contentTypeHeader.ToString(); response.ContentType = resolvedContentType;
if (StatusCode != null) if (StatusCode != null)
{ {
@ -113,8 +108,7 @@ namespace Microsoft.AspNet.Mvc
} }
logger.JsonResultExecuting(Value); logger.JsonResultExecuting(Value);
using (var writer = new HttpResponseStreamWriter(response.Body, resolvedContentTypeEncoding))
using (var writer = new HttpResponseStreamWriter(response.Body, contentTypeHeader.Encoding))
{ {
using (var jsonWriter = new JsonTextWriter(writer)) using (var jsonWriter = new JsonTextWriter(writer))
{ {

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.Logging; using Microsoft.AspNet.Mvc.Logging;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
@ -90,32 +91,23 @@ namespace Microsoft.AspNet.Mvc
tempData = services.GetRequiredService<ITempDataDictionary>(); tempData = services.GetRequiredService<ITempDataDictionary>();
} }
var contentType = ContentType; string resolvedContentType = null;
if (contentType != null && contentType.Encoding == null) Encoding resolvedContentTypeEncoding = null;
{ ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
// Do not modify the user supplied content type, so copy it instead ContentType,
contentType = contentType.Copy(); response.ContentType,
contentType.Encoding = Encoding.UTF8; ViewExecutor.DefaultContentType,
} out resolvedContentType,
out resolvedContentTypeEncoding);
// Priority list for setting content-type/encoding: response.ContentType = resolvedContentType;
// 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();
if (StatusCode != null) if (StatusCode != null)
{ {
response.StatusCode = StatusCode.Value; response.StatusCode = StatusCode.Value;
} }
var encoding = contentType?.Encoding ?? ViewExecutor.DefaultContentType?.Encoding; using (var writer = new HttpResponseStreamWriter(response.Body, resolvedContentTypeEncoding))
using (var writer = new HttpResponseStreamWriter(response.Body, encoding))
{ {
var viewContext = new ViewContext( var viewContext = new ViewContext(
context, context,

View File

@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Diagnostics; using Microsoft.AspNet.Mvc.Diagnostics;
using Microsoft.AspNet.Mvc.Infrastructure; using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewEngines; using Microsoft.AspNet.Mvc.ViewEngines;
@ -96,7 +97,8 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
/// <param name="viewData">The <see cref="ViewDataDictionary"/>.</param> /// <param name="viewData">The <see cref="ViewDataDictionary"/>.</param>
/// <param name="tempData">The <see cref="ITempDataDictionary"/>.</param> /// <param name="tempData">The <see cref="ITempDataDictionary"/>.</param>
/// <param name="contentType"> /// <param name="contentType">
/// The content-type header value to set in the response. If <c>null</c>, <see cref="DefaultContentType"/> will be used. /// The content-type header value to set in the response. If <c>null</c>,
/// <see cref="DefaultContentType"/> will be used.
/// </param> /// </param>
/// <param name="statusCode"> /// <param name="statusCode">
/// The HTTP status code to set in the response. May be <c>null</c>. /// The HTTP status code to set in the response. May be <c>null</c>.
@ -134,26 +136,23 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var response = actionContext.HttpContext.Response; var response = actionContext.HttpContext.Response;
if (contentType != null && contentType.Encoding == null) string resolvedContentType = null;
{ Encoding resolvedContentTypeEncoding = null;
// Do not modify the user supplied content type, so copy it instead ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
contentType = contentType.Copy(); contentType,
contentType.Encoding = Encoding.UTF8; response.ContentType,
} DefaultContentType,
out resolvedContentType,
out resolvedContentTypeEncoding);
// Priority list for setting content-type: response.ContentType = resolvedContentType;
// 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();
if (statusCode != null) if (statusCode != null)
{ {
response.StatusCode = statusCode.Value; response.StatusCode = statusCode.Value;
} }
var encoding = contentType?.Encoding ?? DefaultContentType.Encoding; using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
using (var writer = WriterFactory.CreateWriter(response.Body, encoding))
{ {
var viewContext = new ViewContext( var viewContext = new ViewContext(
actionContext, actionContext,
@ -162,7 +161,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
tempData, tempData,
writer, writer,
ViewOptions.HtmlHelperOptions); ViewOptions.HtmlHelperOptions);
DiagnosticSource.BeforeView(view, viewContext); DiagnosticSource.BeforeView(view, viewContext);
await view.RenderAsync(viewContext); await view.RenderAsync(viewContext);

View File

@ -22,47 +22,6 @@ namespace Microsoft.AspNet.Mvc
{ {
public class ContentResultTest 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] [Fact]
public async Task ContentResult_Response_NullContent_SetsContentTypeAndEncoding() public async Task ContentResult_Response_NullContent_SetsContentTypeAndEncoding()
{ {
@ -118,6 +77,7 @@ namespace Microsoft.AspNet.Mvc
{ {
get get
{ {
// contentType, content, responseContentType, expectedContentType, expectedData
return new TheoryData<MediaTypeHeaderValue, string, string, string, byte[]> return new TheoryData<MediaTypeHeaderValue, string, string, string, byte[]>
{ {
{ {
@ -131,14 +91,14 @@ namespace Microsoft.AspNet.Mvc
new MediaTypeHeaderValue("text/foo"), new MediaTypeHeaderValue("text/foo"),
"κόσμε", "κόσμε",
null, 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 new byte[] { 206, 186, 225, 189, 185, 207, 131, 206, 188, 206, 181 } //utf-8 without BOM
}, },
{ {
MediaTypeHeaderValue.Parse("text/foo;p1=p1-value"), MediaTypeHeaderValue.Parse("text/foo;p1=p1-value"),
"κόσμε", "κόσμε",
null, 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 new byte[] { 206, 186, 225, 189, 185, 207, 131, 206, 188, 206, 181 } //utf-8 without BOM
}, },
{ {

View File

@ -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<MediaTypeHeaderValue, string, string> ResponseContentTypeData
{
get
{
// contentType, responseContentType, expectedContentType
return new TheoryData<MediaTypeHeaderValue, string, string>
{
// 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);
}
}
}

View File

@ -45,7 +45,7 @@ namespace Microsoft.AspNet.Mvc
} }
[Fact] [Fact]
public async Task ExecuteResultAsync_NullEncoding_SetsContentTypeAndDefaultEncoding() public async Task ExecuteResultAsync_NullEncoding_DoesNotSetCharsetOnContentType()
{ {
// Arrange // Arrange
var expected = _abcdUTF8Bytes; var expected = _abcdUTF8Bytes;
@ -62,7 +62,7 @@ namespace Microsoft.AspNet.Mvc
// Assert // Assert
Assert.Equal(expected, written); Assert.Equal(expected, written);
Assert.Equal("text/json; charset=utf-8", context.Response.ContentType); Assert.Equal("text/json", context.Response.ContentType);
} }
[Fact] [Fact]
@ -89,6 +89,52 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal("text/json; charset=us-ascii", context.Response.ContentType); 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<byte> AbcdIndentedUTF8Bytes private static List<byte> AbcdIndentedUTF8Bytes
{ {
get get

View File

@ -201,7 +201,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
} }
[Fact] [Fact]
public async Task ContentResult_WritesContent_SetsContentTypeWithDefaultEncoding() public async Task ContentResult_WritesContent_SetsContentType_UsesDefaultEncoding_AndNoCharset()
{ {
// Arrange // Arrange
var request = new HttpRequestMessage( var request = new HttpRequestMessage(
@ -214,7 +214,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Assert // Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); 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()); Assert.Equal("content", await response.Content.ReadAsStringAsync());
} }

View File

@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc
}; };
var actionContext = CreateActionContext(descriptor); var actionContext = CreateActionContext(descriptor);
var viewComponentResult = new ViewComponentResult var viewComponentResult = new ViewComponentResult
{ {
Arguments = new object[] { "World!" }, Arguments = new object[] { "World!" },
@ -338,11 +338,11 @@ namespace Microsoft.AspNet.Mvc
}, },
{ {
new MediaTypeHeaderValue("text/foo"), new MediaTypeHeaderValue("text/foo"),
"text/foo; charset=utf-8" "text/foo"
}, },
{ {
MediaTypeHeaderValue.Parse("text/foo;p1=p1-value"), 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 }, new MediaTypeHeaderValue("text/foo") { Encoding = Encoding.ASCII },
@ -422,8 +422,13 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(expectedContentType, actionContext.HttpContext.Response.ContentType); Assert.Equal(expectedContentType, actionContext.HttpContext.Response.ContentType);
} }
[Fact] [Theory]
public async Task ViewComponentResult_NoContentTypeSet_PreservesResponseContentType() [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 // Arrange
var descriptor = new ViewComponentDescriptor() var descriptor = new ViewComponentDescriptor()
@ -435,7 +440,6 @@ namespace Microsoft.AspNet.Mvc
var actionContext = CreateActionContext(descriptor); var actionContext = CreateActionContext(descriptor);
var expectedContentType = "application/x-will-not-be-overridden";
actionContext.HttpContext.Response.ContentType = expectedContentType; actionContext.HttpContext.Response.ContentType = expectedContentType;
var viewComponentResult = new ViewComponentResult() var viewComponentResult = new ViewComponentResult()
@ -453,7 +457,7 @@ namespace Microsoft.AspNet.Mvc
} }
private IServiceCollection CreateServices(object diagnosticListener, HttpContext context, params ViewComponentDescriptor[] descriptors) private IServiceCollection CreateServices(object diagnosticListener, HttpContext context, params ViewComponentDescriptor[] descriptors)
{ {
var httpContext = new HttpContextAccessor() { HttpContext = context }; var httpContext = new HttpContextAccessor() { HttpContext = context };
var tempDataProvider = new SessionStateTempDataProvider(); var tempDataProvider = new SessionStateTempDataProvider();
var diagnosticSource = new DiagnosticListener("Microsoft.AspNet"); var diagnosticSource = new DiagnosticListener("Microsoft.AspNet");

View File

@ -70,7 +70,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
// Assert // Assert
Assert.Equal(viewName, viewEngineResult.ViewName); Assert.Equal(viewName, viewEngineResult.ViewName);
} }
[Fact] [Fact]
public void FindView_WritesDiagnostic_ViewFound() public void FindView_WritesDiagnostic_ViewFound()
{ {
@ -163,7 +163,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
await executor.ExecuteAsync(context, Mock.Of<IView>(), viewResult); await executor.ExecuteAsync(context, Mock.Of<IView>(), viewResult);
// Assert // 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. // 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, // 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()); return new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
} }
private PartialViewResultExecutor GetViewExecutor(DiagnosticSource diagnosticSource = null) private PartialViewResultExecutor GetViewExecutor(DiagnosticSource diagnosticSource = null)
{ {
if (diagnosticSource == null) if (diagnosticSource == null)

View File

@ -38,17 +38,22 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
new MediaTypeHeaderValue("text/foo"), new MediaTypeHeaderValue("text/foo"),
null, 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"), MediaTypeHeaderValue.Parse("text/foo; p1=p1-value"),
null, 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, null,
"text/foo; charset=us-ascii" "text/foo; p1=p1-value; charset=us-ascii"
}, },
{ {
null, null,
@ -57,19 +62,24 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
}, },
{ {
null, null,
"application/xml; charset=us-ascii", "text/bar; p1=p1-value",
"application/xml; charset=us-ascii" "text/bar; p1=p1-value"
}, },
{ {
null, null,
"Invalid content type", "text/bar; p1=p1-value; charset=us-ascii",
"Invalid content type" "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/bar",
"text/foo; charset=us-ascii" "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, context,
new RouteData(), new RouteData(),
new ActionDescriptor()); new ActionDescriptor());
context.RequestServices = GetServiceProvider(); context.RequestServices = GetServiceProvider();
var viewExecutor = CreateViewExecutor(); var viewExecutor = CreateViewExecutor();

View File

@ -163,7 +163,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
await executor.ExecuteAsync(context, Mock.Of<IView>(), viewResult); await executor.ExecuteAsync(context, Mock.Of<IView>(), viewResult);
// Assert // 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. // 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, // 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()); return new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
} }
private ViewResultExecutor GetViewExecutor(DiagnosticListener diagnosticSource = null) private ViewResultExecutor GetViewExecutor(DiagnosticListener diagnosticSource = null)
{ {
if (diagnosticSource == null) if (diagnosticSource == null)