diff --git a/samples/MvcSample.Web/Filters/ErrorMessagesAttribute.cs b/samples/MvcSample.Web/Filters/ErrorMessagesAttribute.cs
index e4a5f08da3..1ca581d59b 100644
--- a/samples/MvcSample.Web/Filters/ErrorMessagesAttribute.cs
+++ b/samples/MvcSample.Web/Filters/ErrorMessagesAttribute.cs
@@ -2,6 +2,7 @@
// 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 MvcSample.Web
{
@@ -15,7 +16,7 @@ public override void OnActionExecuted(ActionExecutedContext context)
context.Result = new ContentResult
{
- ContentType = "text/plain",
+ ContentType = new MediaTypeHeaderValue("text/plain"),
Content = "Boom " + context.Exception.Message
};
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ContentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ContentResult.cs
index f2de1256b9..bc4bc9e450 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ContentResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ContentResult.cs
@@ -1,6 +1,8 @@
// 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 System.Threading.Tasks;
using Microsoft.AspNet.Http;
@@ -11,11 +13,20 @@ namespace Microsoft.AspNet.Mvc
{
public class ContentResult : ActionResult
{
- public string Content { get; set; }
+ private readonly MediaTypeHeaderValue DefaultContentType = new MediaTypeHeaderValue("text/plain")
+ {
+ Encoding = Encodings.UTF8EncodingWithoutBOM
+ };
- public Encoding ContentEncoding { get; set; }
+ ///
+ /// Gets or set the content representing the body of the response.
+ ///
+ public string Content { get; set; }
- public string ContentType { get; set; }
+ ///
+ /// Gets or sets the representing the Content-Type header of the response.
+ ///
+ public MediaTypeHeaderValue ContentType { get; set; }
///
/// Gets or sets the HTTP status code.
@@ -26,17 +37,30 @@ public override async Task ExecuteResultAsync([NotNull] ActionContext context)
{
var response = context.HttpContext.Response;
- MediaTypeHeaderValue contentTypeHeader;
- if (string.IsNullOrEmpty(ContentType))
+ var contentTypeHeader = ContentType;
+ Encoding encoding;
+ if (contentTypeHeader == null)
{
- contentTypeHeader = new MediaTypeHeaderValue("text/plain");
+ contentTypeHeader = DefaultContentType;
+ encoding = Encodings.UTF8EncodingWithoutBOM;
}
else
{
- contentTypeHeader = new MediaTypeHeaderValue(ContentType);
+ 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 = Encodings.UTF8EncodingWithoutBOM;
+
+ encoding = Encodings.UTF8EncodingWithoutBOM;
+ }
+ else
+ {
+ encoding = contentTypeHeader.Encoding;
+ }
}
- contentTypeHeader.Encoding = ContentEncoding ?? Encodings.UTF8EncodingWithoutBOM;
response.ContentType = contentTypeHeader.ToString();
if (StatusCode != null)
@@ -46,7 +70,7 @@ public override async Task ExecuteResultAsync([NotNull] ActionContext context)
if (Content != null)
{
- await response.WriteAsync(Content, contentTypeHeader.Encoding);
+ await response.WriteAsync(Content, encoding);
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileContentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileContentResult.cs
index bba491f3da..3fe9b2c578 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileContentResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileContentResult.cs
@@ -6,6 +6,7 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@@ -19,13 +20,14 @@ public class FileContentResult : FileResult
///
/// Creates a new instance with
- /// the provided .
+ /// the provided and the
+ /// provided .
///
/// The bytes that represent the file contents.
- public FileContentResult([NotNull] byte[] fileContents)
- : base(contentType: null)
+ /// The Content-Type header of the response.
+ public FileContentResult([NotNull] byte[] fileContents, [NotNull] string contentType)
+ : this(fileContents, new MediaTypeHeaderValue(contentType))
{
- FileContents = fileContents;
}
///
@@ -35,7 +37,7 @@ public FileContentResult([NotNull] byte[] fileContents)
///
/// The bytes that represent the file contents.
/// The Content-Type header of the response.
- public FileContentResult([NotNull] byte[] fileContents, string contentType)
+ public FileContentResult([NotNull] byte[] fileContents, [NotNull] MediaTypeHeaderValue contentType)
: base(contentType)
{
FileContents = fileContents;
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/FilePathResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/FilePathResult.cs
index 3e727e576c..72e03ca2a2 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/FilePathResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/FilePathResult.cs
@@ -11,6 +11,7 @@
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@@ -27,15 +28,15 @@ public class FilePathResult : FileResult
///
/// Creates a new instance with
- /// the provided
+ /// the provided and the
+ /// provided .
///
/// The path to the file. The path must be an absolute
/// path. Relative and virtual paths are not supported.
/// The Content-Type header of the response.
- public FilePathResult([NotNull] string fileName)
- : base(contentType: null)
+ public FilePathResult([NotNull] string fileName, [NotNull] string contentType)
+ : this(fileName, new MediaTypeHeaderValue(contentType))
{
- FileName = fileName;
}
///
@@ -46,7 +47,7 @@ public FilePathResult([NotNull] string fileName)
/// The path to the file. The path must be an absolute
/// path. Relative and virtual paths are not supported.
/// The Content-Type header of the response.
- public FilePathResult([NotNull] string fileName, string contentType)
+ public FilePathResult([NotNull] string fileName, [NotNull] MediaTypeHeaderValue contentType)
: base(contentType)
{
FileName = fileName;
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileResult.cs
index 0a76eb35e7..814be3e544 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileResult.cs
@@ -24,15 +24,25 @@ public abstract class FileResult : ActionResult
/// the provided .
///
/// The Content-Type header of the response.
- protected FileResult(string contentType)
+ protected FileResult([NotNull] string contentType)
+ : this(new MediaTypeHeaderValue(contentType))
+ {
+ }
+
+ ///
+ /// Creates a new instance with
+ /// the provided .
+ ///
+ /// The Content-Type header of the response.
+ protected FileResult([NotNull] MediaTypeHeaderValue contentType)
{
ContentType = contentType;
}
///
- /// Gets or sets the Content-Type header value that will be written to the response.
+ /// Gets the representing the Content-Type header of the response.
///
- public string ContentType { get; set; }
+ public MediaTypeHeaderValue ContentType { get; }
///
/// Gets the file name that will be used in the Content-Disposition header of the response.
@@ -47,11 +57,7 @@ public string FileDownloadName
public override Task ExecuteResultAsync([NotNull] ActionContext context)
{
var response = context.HttpContext.Response;
-
- if (ContentType != null)
- {
- response.ContentType = ContentType;
- }
+ response.ContentType = ContentType.ToString();
if (!string.IsNullOrEmpty(FileDownloadName))
{
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileStreamResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileStreamResult.cs
index 28b8e8a1d8..68c932c227 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileStreamResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/FileStreamResult.cs
@@ -7,6 +7,7 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@@ -23,13 +24,14 @@ public class FileStreamResult : FileResult
///
/// Creates a new instance with
- /// the provided .
+ /// the provided and the
+ /// provided .
///
/// The stream with the file.
- public FileStreamResult([NotNull] Stream fileStream)
- : base(contentType: null)
+ /// The Content-Type header of the response.
+ public FileStreamResult([NotNull] Stream fileStream, [NotNull] string contentType)
+ : this(fileStream, new MediaTypeHeaderValue(contentType))
{
- FileStream = fileStream;
}
///
@@ -39,7 +41,7 @@ public FileStreamResult([NotNull] Stream fileStream)
///
/// The stream with the file.
/// The Content-Type header of the response.
- public FileStreamResult([NotNull] Stream fileStream, string contentType)
+ public FileStreamResult([NotNull] Stream fileStream, [NotNull] MediaTypeHeaderValue contentType)
: base(contentType)
{
FileStream = fileStream;
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/PartialViewResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/PartialViewResult.cs
index d7045e3b56..36c06b855c 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/PartialViewResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/PartialViewResult.cs
@@ -7,6 +7,7 @@
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
using Microsoft.Framework.Logging;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@@ -45,6 +46,11 @@ public class PartialViewResult : ActionResult
/// ActionContext.HttpContext.RequestServices is used.
public IViewEngine ViewEngine { get; set; }
+ ///
+ /// Gets or sets the representing the Content-Type header of the response.
+ ///
+ public MediaTypeHeaderValue ContentType { get; set; }
+
///
public override async Task ExecuteResultAsync([NotNull] ActionContext context)
{
@@ -74,7 +80,7 @@ public override async Task ExecuteResultAsync([NotNull] ActionContext context)
using (view as IDisposable)
{
- await ViewExecutor.ExecuteAsync(view, context, ViewData, TempData, contentType: null);
+ await ViewExecutor.ExecuteAsync(view, context, ViewData, TempData, ContentType);
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewExecutor.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewExecutor.cs
index 1cf47cc753..8d0d049d53 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewExecutor.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewExecutor.cs
@@ -1,10 +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 System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Framework.Internal;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@@ -14,7 +17,10 @@ namespace Microsoft.AspNet.Mvc
public static class ViewExecutor
{
private const int BufferSize = 1024;
- private const string ContentType = "text/html; charset=utf-8";
+ private static readonly MediaTypeHeaderValue DefaultContentType = new MediaTypeHeaderValue("text/html")
+ {
+ Encoding = Encodings.UTF8EncodingWithoutBOM
+ };
///
/// Asynchronously renders the specified to the response body.
@@ -23,21 +29,43 @@ public static class ViewExecutor
/// The for the current executing action.
/// The for the view being rendered.
/// The for the view being rendered.
- /// A that represents the asychronous rendering.
+ /// A that represents the asynchronous rendering.
public static async Task ExecuteAsync([NotNull] IView view,
[NotNull] ActionContext actionContext,
[NotNull] ViewDataDictionary viewData,
[NotNull] ITempDataDictionary tempData,
- string contentType)
+ MediaTypeHeaderValue contentType)
{
- if (string.IsNullOrEmpty(contentType))
+ var response = actionContext.HttpContext.Response;
+
+ var contentTypeHeader = contentType;
+ Encoding encoding;
+ if (contentTypeHeader == null)
{
- contentType = ContentType;
+ contentTypeHeader = DefaultContentType;
+ encoding = Encodings.UTF8EncodingWithoutBOM;
}
+ else
+ {
+ 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 = Encodings.UTF8EncodingWithoutBOM;
+
+ encoding = Encodings.UTF8EncodingWithoutBOM;
+ }
+ else
+ {
+ encoding = contentTypeHeader.Encoding;
+ }
+ }
+
+ response.ContentType = contentTypeHeader.ToString();
+
+ var wrappedStream = new StreamWrapper(response.Body);
- actionContext.HttpContext.Response.ContentType = contentType;
- var wrappedStream = new StreamWrapper(actionContext.HttpContext.Response.Body);
- var encoding = Encodings.UTF8EncodingWithoutBOM;
using (var writer = new StreamWriter(wrappedStream, encoding, BufferSize, leaveOpen: true))
{
try
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs
index a0bd1cae49..a29f87bbf0 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs
@@ -7,6 +7,7 @@
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
using Microsoft.Framework.Logging;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@@ -45,6 +46,11 @@ public class ViewResult : ActionResult
/// ActionContext.HttpContext.RequestServices is used.
public IViewEngine ViewEngine { get; set; }
+ ///
+ /// Gets or sets the representing the Content-Type header of the response.
+ ///
+ public MediaTypeHeaderValue ContentType { get; set; }
+
///
public override async Task ExecuteResultAsync([NotNull] ActionContext context)
{
@@ -74,7 +80,7 @@ public override async Task ExecuteResultAsync([NotNull] ActionContext context)
using (view as IDisposable)
{
- await ViewExecutor.ExecuteAsync(view, context, ViewData, TempData, contentType: null);
+ await ViewExecutor.ExecuteAsync(view, context, ViewData, TempData, ContentType);
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs
index 4143a6acdf..2cf4f110da 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs
@@ -14,6 +14,7 @@
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.Internal;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@@ -330,7 +331,7 @@ public virtual PartialViewResult PartialView(string viewName, object model)
[NonAction]
public virtual ContentResult Content(string content)
{
- return Content(content, contentType: null);
+ return Content(content, (MediaTypeHeaderValue)null);
}
///
@@ -356,11 +357,23 @@ public virtual ContentResult Content(string content, string contentType)
/// The created object for the response.
[NonAction]
public virtual ContentResult Content(string content, string contentType, Encoding contentEncoding)
+ {
+ return Content(content, new MediaTypeHeaderValue(contentType) { Encoding = contentEncoding });
+ }
+
+ ///
+ /// Creates a object by specifying a
+ /// string and a .
+ ///
+ /// The content to write to the response.
+ /// The content type (MIME type).
+ /// The created object for the response.
+ [NonAction]
+ public virtual ContentResult Content(string content, MediaTypeHeaderValue contentType)
{
var result = new ContentResult
{
Content = content,
- ContentEncoding = contentEncoding,
ContentType = contentType
};
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ContentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ContentResultTest.cs
index 0b5e56f797..b1b3ae6ebc 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ContentResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ContentResultTest.cs
@@ -1,11 +1,12 @@
// 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 System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
+using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@@ -20,8 +21,7 @@ public async Task ContentResult_Response_NullEncoding_SetsContentTypeAndDefaultE
var contentResult = new ContentResult
{
Content = "Test Content",
- ContentType = "application/json",
- ContentEncoding = null
+ ContentType = new MediaTypeHeaderValue("application/json")
};
var httpContext = GetHttpContext();
var actionContext = GetActionContext(httpContext);
@@ -40,8 +40,10 @@ public async Task ContentResult_Response_SetsContentTypeAndEncoding()
var contentResult = new ContentResult
{
Content = "Test Content",
- ContentType = "text/plain",
- ContentEncoding = Encoding.ASCII
+ ContentType = new MediaTypeHeaderValue("text/plain")
+ {
+ Encoding = Encoding.ASCII
+ }
};
var httpContext = GetHttpContext();
var actionContext = GetActionContext(httpContext);
@@ -54,14 +56,16 @@ public async Task ContentResult_Response_SetsContentTypeAndEncoding()
}
[Fact]
- public async Task ContentResult_Response_NullContentType_SetsEncodingAndDefaultContentType()
+ public async Task ContentResult_Response_NullContent_SetsContentTypeAndEncoding()
{
// Arrange
var contentResult = new ContentResult
{
- Content = "Test Content",
- ContentType = null,
- ContentEncoding = Encoding.UTF7
+ Content = null,
+ ContentType = new MediaTypeHeaderValue("text/plain")
+ {
+ Encoding = Encoding.UTF7
+ }
};
var httpContext = GetHttpContext();
var actionContext = GetActionContext(httpContext);
@@ -73,45 +77,65 @@ public async Task ContentResult_Response_NullContentType_SetsEncodingAndDefaultC
Assert.Equal("text/plain; charset=utf-7", httpContext.Response.ContentType);
}
- [Fact]
- public async Task ContentResult_Response_NullContent_SetsContentTypeAndEncoding()
+ public static TheoryData ContentResultContentTypeData
{
- // Arrange
- var contentResult = new ContentResult
+ get
{
- Content = null,
- ContentType = "application/json",
- ContentEncoding = Encoding.UTF8
- };
- var httpContext = GetHttpContext();
- var actionContext = GetActionContext(httpContext);
-
- // Act
- await contentResult.ExecuteResultAsync(actionContext);
-
- // Assert
- Assert.Equal("application/json; charset=utf-8", httpContext.Response.ContentType);
+ return new TheoryData
+ {
+ {
+ null,
+ "κόσμε",
+ "text/plain; charset=utf-8",
+ new byte[] { 206, 186, 225, 189, 185, 207, 131, 206, 188, 206, 181 } //utf-8 without BOM
+ },
+ {
+ new MediaTypeHeaderValue("text/foo"),
+ "κόσμε",
+ "text/foo; charset=utf-8",
+ new byte[] { 206, 186, 225, 189, 185, 207, 131, 206, 188, 206, 181 } //utf-8 without BOM
+ },
+ {
+ MediaTypeHeaderValue.Parse("text/foo;p1=p1-value"),
+ "κόσμε",
+ "text/foo; p1=p1-value; charset=utf-8",
+ new byte[] { 206, 186, 225, 189, 185, 207, 131, 206, 188, 206, 181 } //utf-8 without BOM
+ },
+ {
+ new MediaTypeHeaderValue("text/foo") { Encoding = Encoding.ASCII },
+ "abcd",
+ "text/foo; charset=us-ascii",
+ new byte[] { 97, 98, 99, 100 }
+ }
+ };
+ }
}
- [Fact]
- public async Task ContentResult_Response_BadContentType_ThrowsFormatException()
+ [Theory]
+ [MemberData(nameof(ContentResultContentTypeData))]
+ public async Task ContentResult_ExecuteResultAsync_SetContentTypeAndEncoding_OnResponse(
+ MediaTypeHeaderValue contentType,
+ string content,
+ string expectedContentType,
+ byte[] expectedContentData)
{
// Arrange
var contentResult = new ContentResult
{
- Content = "Test Content",
- ContentType = "some-type",
- ContentEncoding = null
+ Content = content,
+ ContentType = contentType
};
var httpContext = GetHttpContext();
+ var memoryStream = new MemoryStream();
+ httpContext.Response.Body = memoryStream;
var actionContext = GetActionContext(httpContext);
// Act
- var exception = await Assert.ThrowsAsync(
- async () => await contentResult.ExecuteResultAsync(actionContext));
+ await contentResult.ExecuteResultAsync(actionContext);
// Assert
- Assert.Equal("Invalid media type 'some-type'.", exception.Message);
+ Assert.Equal(expectedContentType, httpContext.Response.ContentType);
+ Assert.Equal(expectedContentData, memoryStream.ToArray());
}
private static ActionContext GetActionContext(HttpContext httpContext)
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileContentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileContentResultTest.cs
index ae4037f03b..a53aec0c34 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileContentResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileContentResultTest.cs
@@ -5,6 +5,7 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
+using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNet.Mvc
@@ -45,5 +46,29 @@ public async Task WriteFileAsync_CopiesBuffer_ToOutputStream()
// Assert
Assert.Equal(buffer, outStream.ToArray());
}
+
+ [Fact]
+ public async Task ExecuteResultAsync_SetsSuppliedContentTypeAndEncoding()
+ {
+ // Arrange
+ var expectedContentType = "text/foo; charset=us-ascii";
+ var buffer = new byte[] { 1, 2, 3, 4, 5 };
+
+ var httpContext = new DefaultHttpContext();
+
+ var outStream = new MemoryStream();
+ httpContext.Response.Body = outStream;
+
+ var context = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
+
+ var result = new FileContentResult(buffer, MediaTypeHeaderValue.Parse(expectedContentType));
+
+ // Act
+ await result.ExecuteResultAsync(context);
+
+ // Assert
+ Assert.Equal(buffer, outStream.ToArray());
+ Assert.Equal(expectedContentType, httpContext.Response.ContentType);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FilePathResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FilePathResultTest.cs
index 651aecdc4b..80342a13da 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FilePathResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FilePathResultTest.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.FileProviders;
@@ -9,9 +10,9 @@
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
+using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
-using System.Text;
namespace Microsoft.AspNet.Mvc
{
@@ -111,6 +112,36 @@ public async Task ExecuteResultAsync_CallsSendFileAsync_IfIHttpSendFilePresent()
sendFileMock.Verify();
}
+ [Fact]
+ public async Task ExecuteResultAsync_SetsSuppliedContentTypeAndEncoding()
+ {
+ // Arrange
+ var expectedContentType = "text/foo; charset=us-ascii";
+ // path will be C:/.../TestFiles/FilePathResultTestFile_ASCII.txt
+ var path = Path.GetFullPath(Path.Combine(".", "TestFiles", "FilePathResultTestFile_ASCII.txt"));
+ path = path.Replace(@"\", "/");
+
+ // Point the FileProviderRoot to a subfolder
+ var result = new FilePathResult(path, MediaTypeHeaderValue.Parse(expectedContentType))
+ {
+ FileProvider = new PhysicalFileProvider(Path.GetFullPath("Utils")),
+ };
+
+ var httpContext = new DefaultHttpContext();
+ var memoryStream = new MemoryStream();
+ httpContext.Response.Body = memoryStream;
+
+ var context = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
+
+ // Act
+ await result.ExecuteResultAsync(context);
+
+ // Assert
+ var contents = Encoding.ASCII.GetString(memoryStream.ToArray());
+ Assert.Equal("FilePathResultTestFile contents ASCII encoded", contents);
+ Assert.Equal(expectedContentType, httpContext.Response.ContentType);
+ }
+
[Fact]
public async Task ExecuteResultAsync_WorksWithAbsolutePaths_UsingBackSlash()
{
@@ -186,7 +217,7 @@ public async Task ExecuteResultAsync_WorksWithNonDiskBasedFiles()
nonDiskFileInfo.Setup(fi => fi.CreateReadStream()).Returns(sourceStream);
var nonDiskFileProvider = new Mock();
nonDiskFileProvider.Setup(fp => fp.GetFileInfo(It.IsAny())).Returns(nonDiskFileInfo.Object);
- var filePathResult = new FilePathResult("/SampleEmbeddedFile.txt")
+ var filePathResult = new FilePathResult("/SampleEmbeddedFile.txt", "text/plain")
{
FileProvider = nonDiskFileProvider.Object
};
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileResultTest.cs
index 4c818ffac4..281a25f2bb 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileResultTest.cs
@@ -22,7 +22,7 @@ public void Constructor_SetsContentType()
var result = new EmptyFileResult("text/plain");
// Assert
- Assert.Equal("text/plain", result.ContentType);
+ Assert.Equal("text/plain", result.ContentType.ToString());
}
[Fact]
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileStreamResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileStreamResultTest.cs
index b08597819e..1be9797f29 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileStreamResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/FileStreamResultTest.cs
@@ -7,6 +7,7 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
+using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@@ -87,5 +88,34 @@ public async Task WriteFileAsync_CopiesProvidedStream_ToOutputStream()
var outBytes = outStream.ToArray();
Assert.True(originalBytes.SequenceEqual(outBytes));
}
+
+ [Fact]
+ public async Task SetsSuppliedContentTypeAndEncoding()
+ {
+ // Arrange
+ var expectedContentType = "text/foo; charset=us-ascii";
+ // Generate an array of bytes with a predictable pattern
+ // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, 13
+ var originalBytes = Enumerable.Range(0, 0x1234)
+ .Select(b => (byte)(b % 20)).ToArray();
+
+ var originalStream = new MemoryStream(originalBytes);
+
+ var httpContext = new DefaultHttpContext();
+ var outStream = new MemoryStream();
+ httpContext.Response.Body = outStream;
+
+ var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
+
+ var result = new FileStreamResult(originalStream, MediaTypeHeaderValue.Parse(expectedContentType));
+
+ // Act
+ await result.ExecuteResultAsync(actionContext);
+
+ // Assert
+ var outBytes = outStream.ToArray();
+ Assert.True(originalBytes.SequenceEqual(outBytes));
+ Assert.Equal(expectedContentType, httpContext.Response.ContentType);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/PartialViewResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/PartialViewResultTest.cs
index 65c181cd83..310bada677 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/PartialViewResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/PartialViewResultTest.cs
@@ -2,11 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.Logging;
+using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@@ -69,6 +71,58 @@ public async Task ViewResult_UsesFindPartialViewOnSpecifiedViewEngineToLocateVie
viewEngine.Verify();
}
+ public static TheoryData PartialViewResultContentTypeData
+ {
+ get
+ {
+ return new TheoryData
+ {
+ {
+ null,
+ "text/html; charset=utf-8"
+ },
+ {
+ new MediaTypeHeaderValue("text/foo"),
+ "text/foo; charset=utf-8"
+ },
+ {
+ new MediaTypeHeaderValue("text/foo") { Encoding = Encoding.ASCII },
+ "text/foo; charset=us-ascii"
+ }
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(PartialViewResultContentTypeData))]
+ public async Task PartialViewResult_SetsContentTypeHeader(
+ MediaTypeHeaderValue contentType,
+ string expectedContentTypeHeaderValue)
+ {
+ // Arrange
+ var viewName = "myview";
+ var httpContext = GetHttpContext();
+ var context = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
+ var viewEngine = new Mock();
+ var view = Mock.Of();
+
+ viewEngine.Setup(e => e.FindPartialView(context, "myview"))
+ .Returns(ViewEngineResult.Found("myview", view));
+
+ var viewResult = new PartialViewResult
+ {
+ ViewName = viewName,
+ ViewEngine = viewEngine.Object,
+ ContentType = contentType
+ };
+
+ // Act
+ await viewResult.ExecuteResultAsync(context);
+
+ // Assert
+ Assert.Equal(expectedContentTypeHeaderValue, httpContext.Response.ContentType);
+ }
+
[Fact]
public async Task ExecuteResultAsync_UsesActionDescriptorName_IfViewNameIsNull()
{
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ViewExecutorTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ViewExecutorTest.cs
index b63d53e150..0af2c87812 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ViewExecutorTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ViewExecutorTest.cs
@@ -4,11 +4,13 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Routing;
+using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@@ -19,12 +21,44 @@ public class ViewExecutorTest
// The buffer size of the StreamWriter used in ViewResult.
private const int ViewResultStreamWriterBufferSize = 1024;
- [Fact]
- public async Task ExecuteAsync_WritesOutputWithoutBOM()
+ public static TheoryData ViewExecutorSetsContentTypeAndEncodingData
{
- // Arrange
- var expected = new byte[] { 97, 98, 99, 100 };
+ get
+ {
+ return new TheoryData
+ {
+ {
+ null,
+ "text/html; charset=utf-8",
+ new byte[] { 97, 98, 99, 100 }
+ },
+ {
+ new MediaTypeHeaderValue("text/foo"),
+ "text/foo; charset=utf-8",
+ new byte[] { 97, 98, 99, 100 }
+ },
+ {
+ MediaTypeHeaderValue.Parse("text/foo; p1=p1-value"),
+ "text/foo; p1=p1-value; charset=utf-8",
+ new byte[] { 97, 98, 99, 100 }
+ },
+ {
+ new MediaTypeHeaderValue("text/foo") { Charset = "us-ascii" },
+ "text/foo; charset=us-ascii",
+ new byte[] { 97, 98, 99, 100 }
+ }
+ };
+ }
+ }
+ [Theory]
+ [MemberData(nameof(ViewExecutorSetsContentTypeAndEncodingData))]
+ public async Task ExecuteAsync_SetsContentTypeAndEncoding(
+ MediaTypeHeaderValue contentType,
+ string expectedContentType,
+ byte[] expectedContentData)
+ {
+ // Arrange
var view = new Mock();
view.Setup(v => v.RenderAsync(It.IsAny()))
.Callback((ViewContext v) =>
@@ -44,33 +78,11 @@ public async Task ExecuteAsync_WritesOutputWithoutBOM()
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
// Act
- await ViewExecutor.ExecuteAsync(view.Object, actionContext, viewData, null, contentType: null);
-
- // Assert
- Assert.Equal(expected, memoryStream.ToArray());
- Assert.Equal("text/html; charset=utf-8", context.Response.ContentType);
- }
-
- [Fact]
- public async Task ExecuteAsync_UsesSpecifiedContentType()
- {
- // Arrange
- var contentType = "some-content-type";
- var view = Mock.Of();
- var context = new DefaultHttpContext();
- var memoryStream = new MemoryStream();
- context.Response.Body = memoryStream;
-
- var actionContext = new ActionContext(context,
- new RouteData(),
- new ActionDescriptor());
- var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
-
- // Act
- await ViewExecutor.ExecuteAsync(view, actionContext, viewData, null, contentType);
+ await ViewExecutor.ExecuteAsync(view.Object, actionContext, viewData, null, contentType);
// Assert
- Assert.Equal(contentType, context.Response.ContentType);
+ Assert.Equal(expectedContentType, context.Response.ContentType);
+ Assert.Equal(expectedContentData, memoryStream.ToArray());
}
public static IEnumerable