diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs index e08443c6b0..81ef066f9d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs @@ -177,6 +177,9 @@ namespace Microsoft.AspNet.Mvc throw new InvalidOperationException(Resources.FormatOutputFormatterNoMediaType(GetType().FullName)); } + // Clone the media type as we don't want it to affect the next request + selectedMediaType = MediaTypeHeaderValue.Parse(selectedMediaType.ToString()); + var selectedEncoding = SelectCharacterEncoding(context); if (selectedEncoding == null) { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/OutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/OutputFormatterTests.cs index e2e7cbe1f1..80e90c72d7 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/OutputFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/OutputFormatterTests.cs @@ -120,6 +120,29 @@ namespace Microsoft.AspNet.Mvc.Test formatterContext.SelectedContentType.ToString()); } + [Fact] + public async Task WriteResponseHeaders_ClonesMediaType() + { + // Arrange + var formatter = new PngImageFormatter(); + formatter.SupportedMediaTypes.Clear(); + var mediaType = new MediaTypeHeaderValue("image/png"); + formatter.SupportedMediaTypes.Add(mediaType); + var formatterContext = new OutputFormatterContext(); + formatterContext.ActionContext = new ActionContext( + new DefaultHttpContext(), + new RouteData(), + new ActionDescriptor()); + + // Act + await formatter.WriteAsync(formatterContext); + + // Assert + Assert.NotSame(mediaType, formatterContext.SelectedContentType); + Assert.Null(mediaType.Charset); + Assert.Equal("image/png; charset=utf-8", formatterContext.SelectedContentType.ToString()); + } + [Fact] public void CanWriteResult_ForNullContentType_UsesFirstEntryInSupportedContentTypes() { @@ -294,7 +317,7 @@ namespace Microsoft.AspNet.Mvc.Test public override bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType) { // Do not set the selected media Type. - // The WriteResponseContentHeader should do it for you. + // The WriteResponseHeaders should do it for you. return true; } @@ -303,5 +326,19 @@ namespace Microsoft.AspNet.Mvc.Test return Task.FromResult(true); } } + + private class PngImageFormatter : OutputFormatter + { + public PngImageFormatter() + { + SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("image/png")); + SupportedEncodings.Add(Encoding.UTF8); + } + + public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context) + { + return Task.FromResult(true); + } + } } } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs index d9c3f0e892..cadcbeb8d9 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs @@ -296,6 +296,50 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal(expectedBody, body); } + [Fact] + public async Task JsonFormatter_SupportedMediaType_DoesNotChangeAcrossRequests() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8"); + var expectedBody = "{\"MethodName\":\"ReturnJsonResult\"}"; + + for (int i = 0; i < 5; i++) + { + // Act and Assert + var response = await client.GetAsync("http://localhost/JsonResult/ReturnJsonResult"); + + Assert.Equal(expectedContentType, response.Content.Headers.ContentType); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(expectedBody, body); + } + } + + [Fact] + public async Task XmlFormatter_SupportedMediaType_DoesNotChangeAcrossRequests() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); + client.DefaultRequestHeaders.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8")); + var expectedContentType = MediaTypeHeaderValue.Parse("application/xml;charset=utf-8"); + var expectedBody = @"
" + + @"One Microsoft Way
John
"; + + for (int i = 0; i < 5; i++) + { + // Act and Assert + var response = await client.GetAsync("http://localhost/Home/UserInfo"); + + Assert.Equal(expectedContentType, response.Content.Headers.ContentType); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(expectedBody, body); + } + } + [Theory] [InlineData("UseTheFallback_WithDefaultFormatters")] [InlineData("UseTheFallback_UsingCustomFormatters")] diff --git a/test/WebSites/ConnegWebSite/Controllers/HomeController.cs b/test/WebSites/ConnegWebSite/Controllers/HomeController.cs index 56307aa2fe..e54e8e8ad0 100644 --- a/test/WebSites/ConnegWebSite/Controllers/HomeController.cs +++ b/test/WebSites/ConnegWebSite/Controllers/HomeController.cs @@ -11,5 +11,10 @@ namespace ConnegWebSite { return new JsonResult("Index Method"); } + + public User UserInfo() + { + return new User() { Name = "John", Address = "One Microsoft Way" }; + } } } \ No newline at end of file diff --git a/test/WebSites/ConnegWebSite/Startup.cs b/test/WebSites/ConnegWebSite/Startup.cs index 8901654343..fd4e143ecf 100644 --- a/test/WebSites/ConnegWebSite/Startup.cs +++ b/test/WebSites/ConnegWebSite/Startup.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.Builder; +using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Routing; using Microsoft.Framework.DependencyInjection; @@ -18,8 +19,13 @@ namespace ConnegWebSite { // Add MVC services to the services container services.AddMvc(configuration); - }); + services.Configure(options => + { + options.AddXmlDataContractSerializerFormatter(); + }); + }); + // Add MVC to the request pipeline app.UseMvc(routes => {