diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ContentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ContentResult.cs index 997a1dadc7..5a3dd9c21a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ContentResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ContentResult.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Http; +using Microsoft.Net.Http.Headers; namespace Microsoft.AspNet.Mvc { @@ -19,14 +20,22 @@ namespace Microsoft.AspNet.Mvc { var response = context.HttpContext.Response; - if (!string.IsNullOrEmpty(ContentType)) + MediaTypeHeaderValue contentTypeHeader; + if (string.IsNullOrEmpty(ContentType)) { - response.ContentType = ContentType; + contentTypeHeader = new MediaTypeHeaderValue("text/plain"); } + else + { + contentTypeHeader = new MediaTypeHeaderValue(ContentType); + } + + contentTypeHeader.Encoding = ContentEncoding ?? Encodings.UTF8EncodingWithoutBOM; + response.ContentType = contentTypeHeader.ToString(); if (Content != null) { - await response.WriteAsync(Content); + await response.WriteAsync(Content, contentTypeHeader.Encoding); } } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ContentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ContentResultTest.cs new file mode 100644 index 0000000000..84920b5170 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ContentResultTest.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Core; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class ContentResultTest + { + [Fact] + public async Task ContentResult_Response_NullEncoding_SetsContentTypeAndDefaultEncoding() + { + // Arrange + var contentResult = new ContentResult + { + Content = "Test Content", + ContentType = "application/json", + ContentEncoding = null + }; + 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 = "text/plain", + ContentEncoding = 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_NullContentType_SetsEncodingAndDefaultContentType() + { + // Arrange + var contentResult = new ContentResult + { + Content = "Test Content", + ContentType = null, + ContentEncoding = Encoding.UTF7 + }; + var httpContext = GetHttpContext(); + var actionContext = GetActionContext(httpContext); + + // Act + await contentResult.ExecuteResultAsync(actionContext); + + // Assert + Assert.Equal("text/plain; charset=utf-7", httpContext.Response.ContentType); + } + + [Fact] + public async Task ContentResult_Response_NullContent_SetsContentTypeAndEncoding() + { + // Arrange + var contentResult = new ContentResult + { + 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); + } + + [Fact] + public async Task ContentResult_Response_BadContentType_ThrowsFormatException() + { + // Arrange + var contentResult = new ContentResult + { + Content = "Test Content", + ContentType = "some-type", + ContentEncoding = null + }; + var httpContext = GetHttpContext(); + var actionContext = GetActionContext(httpContext); + + // Act + var exception = await Assert.ThrowsAsync( + async () => await contentResult.ExecuteResultAsync(actionContext)); + + // Assert + Assert.Equal("Invalid media type 'some-type'.", exception.Message); + } + + private static ActionContext GetActionContext(HttpContext httpContext) + { + var routeData = new RouteData(); + routeData.Routers.Add(Mock.Of()); + + return new ActionContext(httpContext, + routeData, + new ActionDescriptor()); + } + + private static HttpContext GetHttpContext() + { + return new DefaultHttpContext(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs index 7ff7a5170a..48e9b244c4 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs @@ -279,5 +279,65 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests responseContent); Assert.Equal(sampleStringError, errors["test.SampleString"]); } + + [Fact] + public async Task ContentResult_WritesContent_SetsDefaultContentTypeAndEncoding() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetContentResult"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("content", await response.Content.ReadAsStringAsync()); + Assert.Equal("text/plain", response.Content.Headers.ContentType.MediaType); + Assert.Equal("utf-8", response.Content.Headers.ContentType.CharSet); + } + + [Fact] + public async Task ContentResult_WritesContent_SetsContentTypeWithDefaultEncoding() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetContentResultWithContentType"); + + // Act + var response = await client.SendAsync(request); + + // 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()); + } + + [Fact] + public async Task ContentResult_WritesContent_SetsContentTypeAndEncoding() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetContentResultWithContentTypeAndEncoding"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); + Assert.Equal("us-ascii", response.Content.Headers.ContentType.CharSet); + Assert.Equal("content", await response.Content.ReadAsStringAsync()); + } } } \ No newline at end of file diff --git a/test/WebSites/ActionResultsWebSite/Controllers/ActionResultsVerificationController.cs b/test/WebSites/ActionResultsWebSite/Controllers/ActionResultsVerificationController.cs index bffd5cd400..1073799b7c 100644 --- a/test/WebSites/ActionResultsWebSite/Controllers/ActionResultsVerificationController.cs +++ b/test/WebSites/ActionResultsWebSite/Controllers/ActionResultsVerificationController.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; +using System.Text; using Microsoft.AspNet.Mvc; namespace ActionResultsWebSite @@ -61,6 +62,21 @@ namespace ActionResultsWebSite return CreatedAtRoute("custom-route", values, CreateDummy()); } + public IActionResult GetContentResult() + { + return Content("content"); + } + + public IActionResult GetContentResultWithContentType() + { + return Content("content", "application/json"); + } + + public IActionResult GetContentResultWithContentTypeAndEncoding() + { + return Content("content", "application/json", Encoding.ASCII); + } + public DummyClass GetDummy(int id) { return CreateDummy();