[Fixes #3177] Clean up logic for encoding in ViewResult and friends
This commit is contained in:
parent
cd761a644d
commit
5364468001
|
|
@ -47,32 +47,31 @@ namespace Microsoft.AspNet.Mvc
|
|||
var logger = loggerFactory.CreateLogger<ContentResult>();
|
||||
|
||||
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<IHttpBufferingFeature>();
|
||||
bufferingFeature?.DisableResponseBuffering();
|
||||
|
||||
return response.WriteAsync(Content, contentTypeHeader?.Encoding ?? DefaultContentType.Encoding);
|
||||
return response.WriteAsync(Content, resolvedContentTypeEncoding);
|
||||
}
|
||||
|
||||
return TaskCache.CompletedTask;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<ITempDataDictionary>();
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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
|
|||
/// <param name="viewData">The <see cref="ViewDataDictionary"/>.</param>
|
||||
/// <param name="tempData">The <see cref="ITempDataDictionary"/>.</param>
|
||||
/// <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 name="statusCode">
|
||||
/// 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;
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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<MediaTypeHeaderValue, string, string, string, byte[]>
|
||||
{
|
||||
{
|
||||
|
|
@ -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
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<byte> AbcdIndentedUTF8Bytes
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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<IView>(), 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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
|||
await executor.ExecuteAsync(context, Mock.Of<IView>(), 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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue