From 7a3dc352c96831827efde3506dac763253b95c4b Mon Sep 17 00:00:00 2001 From: harshgMSFT Date: Thu, 11 Sep 2014 16:07:34 -0700 Subject: [PATCH] 1. Moving the HttpNotAcceptableOutputFormatter to product code. 2. Renaming the NoContentFormatter to HttpNoContentOutputFormatter. 3. Updating the test to use mock. --- ...ter.cs => HttpNoContentOutputFormatter.cs} | 2 +- .../HttpNotAcceptableOutputFormatter.cs | 39 ++++++++ src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs | 2 +- .../ActionResults/ObjectResultTests.cs | 90 ++++++++----------- .../Formatters/NoContentFormatterTests.cs | 4 +- .../ConnegTests.cs | 2 +- .../MvcOptionSetupTest.cs | 2 +- .../FallbackOnTypeBasedMatchController.cs | 25 +----- 8 files changed, 86 insertions(+), 80 deletions(-) rename src/Microsoft.AspNet.Mvc.Core/Formatters/{NoContentFormatter.cs => HttpNoContentOutputFormatter.cs} (95%) create mode 100644 src/Microsoft.AspNet.Mvc.Core/Formatters/HttpNotAcceptableOutputFormatter.cs diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/NoContentFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/HttpNoContentOutputFormatter.cs similarity index 95% rename from src/Microsoft.AspNet.Mvc.Core/Formatters/NoContentFormatter.cs rename to src/Microsoft.AspNet.Mvc.Core/Formatters/HttpNoContentOutputFormatter.cs index 5d2ab6e833..2416f32979 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/NoContentFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/HttpNoContentOutputFormatter.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Mvc /// /// Sets the status code to 204 if the content is null. /// - public class NoContentFormatter : IOutputFormatter + public class HttpNoContentOutputFormatter : IOutputFormatter { public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType) { diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/HttpNotAcceptableOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/HttpNotAcceptableOutputFormatter.cs new file mode 100644 index 0000000000..556585490f --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/HttpNotAcceptableOutputFormatter.cs @@ -0,0 +1,39 @@ +// 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.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.HeaderValueAbstractions; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// A formatter which does not have a supported content type and selects itself if no content type is passed to it. + /// Sets the status code to 406 if selected. + /// + public class HttpNotAcceptableOutputFormatter : IOutputFormatter + { + /// + public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType) + { + return contentType == null; + } + + /// + public IReadOnlyList GetSupportedContentTypes(Type declaredType, + Type runtimeType, + MediaTypeHeaderValue contentType) + { + return null; + } + + /// + public Task WriteAsync(OutputFormatterContext context) + { + var response = context.ActionContext.HttpContext.Response; + response.StatusCode = 406; + return Task.FromResult(true); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs index e52b291438..3fced5aeb9 100644 --- a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs @@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Mvc options.ModelBinders.Add(new ComplexModelDtoModelBinder()); // Set up default output formatters. - options.OutputFormatters.Add(new NoContentFormatter()); + options.OutputFormatters.Add(new HttpNoContentOutputFormatter()); options.OutputFormatters.Add(new TextPlainFormatter()); options.OutputFormatters.Add(new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), indent: false)); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectResultTests.cs index 2de4f9b5bb..df04df1908 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectResultTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectResultTests.cs @@ -275,18 +275,23 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults } [Theory] - [InlineData("", 2)] - [InlineData(null, 2)] - [InlineData("application/xml", 3)] - [InlineData("application/custom", 3)] - [InlineData("application/xml;q=1, application/custom;q=0.8", 4)] + [InlineData("")] + [InlineData(null)] + [InlineData("application/xml")] + [InlineData("application/custom")] + [InlineData("application/xml;q=1, application/custom;q=0.8")] public void SelectFormatter_WithNoMatchingAcceptHeadersAndRequestContentType_PicksFormatterBasedOnObjectType - (string acceptHeader, int attemptedCountForCanWrite) + (string acceptHeader) { // For no accept headers, // can write is called twice once for the request media type and once for the type match pass. // For each additional accept header, it is called once. // Arrange + var acceptHeaderCollection = string.IsNullOrEmpty(acceptHeader) ? + null : + acceptHeader?.Split(',') + .Select(header => MediaTypeWithQualityHeaderValue.Parse(header)) + .ToArray(); var stream = new MemoryStream(); var httpResponse = new Mock(); httpResponse.SetupProperty(o => o.ContentType); @@ -295,15 +300,10 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults var actionContext = CreateMockActionContext(httpResponse.Object, requestAcceptHeader: acceptHeader, requestContentType: "application/xml"); + var requestContentType = MediaTypeHeaderValue.Parse("application/xml"); var input = "testInput"; var result = new ObjectResult(input); - - // Set more than one formatters. The test output formatter throws on write. - result.Formatters = new List - { - new CannotWriteFormatter(), - new CountingFormatter(), - }; + var mockCountingFormatter = new Mock(); var context = new OutputFormatterContext() { @@ -311,13 +311,37 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults Object = input, DeclaredType = typeof(string) }; + var mockCountingSupportedContentType = MediaTypeHeaderValue.Parse("application/text"); + mockCountingFormatter.Setup(o => o.CanWriteResult(context, + It.IsNotIn(mockCountingSupportedContentType))) + .Returns(false); + mockCountingFormatter.Setup(o => o.CanWriteResult(context, mockCountingSupportedContentType)) + .Returns(true); + + mockCountingFormatter.Setup(o => o.GetSupportedContentTypes(context.DeclaredType, + input.GetType(), + It.IsAny())) + .Returns(new List { mockCountingSupportedContentType }); + + // Set more than one formatters. The test output formatter throws on write. + result.Formatters = new List + { + new CannotWriteFormatter(), + mockCountingFormatter.Object, + }; // Act var formatter = result.SelectFormatter(context, result.Formatters); // Assert - var countingFormatter = Assert.IsType(formatter); - Assert.Equal(attemptedCountForCanWrite, countingFormatter.GetCanWriteCallCount()); + Assert.Equal(mockCountingFormatter.Object, formatter); + mockCountingFormatter.Verify(v => v.CanWriteResult(context, + mockCountingSupportedContentType), + Times.Once()); + var callCount = (acceptHeaderCollection == null ? 0 : acceptHeaderCollection.Count()) + 1; + mockCountingFormatter.Verify(v => v.CanWriteResult(context, + It.IsNotIn(mockCountingSupportedContentType)), + Times.Exactly(callCount)); } [Fact] @@ -507,42 +531,6 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults return serviceCollection.BuildServiceProvider(); } - public class CountingFormatter : OutputFormatter - { - private int _canWriteCallsCount = 0; - - public CountingFormatter() - { - SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/plain")); - SupportedEncodings.Add(Encoding.GetEncoding("utf-8")); - } - - public override bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType) - { - _canWriteCallsCount++; - if (base.CanWriteResult(context, contentType)) - { - var actionReturnString = context.Object as string; - if (actionReturnString != null) - { - return true; - } - } - - return false; - } - - public int GetCanWriteCallCount() - { - return _canWriteCallsCount; - } - - public override Task WriteResponseBodyAsync(OutputFormatterContext context) - { - throw new NotImplementedException(); - } - } - public class CannotWriteFormatter : IOutputFormatter { public virtual bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType) diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/NoContentFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/NoContentFormatterTests.cs index 899190f4ff..0210e3fb55 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/NoContentFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/NoContentFormatterTests.cs @@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc.Test ActionContext = null, }; var contetType = useNonNullContentType ? MediaTypeHeaderValue.Parse("text/plain") : null; - var formatter = new NoContentFormatter(); + var formatter = new HttpNoContentOutputFormatter(); // Act var actualCanWriteResult = formatter.CanWriteResult(formatterContext, contetType); @@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.Test ActionContext = new ActionContext(defaultHttpContext, new RouteData(), new ActionDescriptor()) }; - var formatter = new NoContentFormatter(); + var formatter = new HttpNoContentOutputFormatter(); // Act await formatter.WriteAsync(formatterContext); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs index a33a09ada6..b1fd58cc6f 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs @@ -3,7 +3,7 @@ using System; using System.Net; -using System.Net.Http.Headers; +using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs index 062ccc50b4..823d541ab5 100644 --- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs @@ -74,7 +74,7 @@ namespace Microsoft.AspNet.Mvc // Assert Assert.Equal(5, mvcOptions.OutputFormatters.Count); - Assert.IsType(mvcOptions.OutputFormatters[0].Instance); + Assert.IsType(mvcOptions.OutputFormatters[0].Instance); Assert.IsType(mvcOptions.OutputFormatters[1].Instance); Assert.IsType(mvcOptions.OutputFormatters[2].Instance); Assert.IsType(mvcOptions.OutputFormatters[3].Instance); diff --git a/test/WebSites/ConnegWebSite/Controllers/FallbackOnTypeBasedMatchController.cs b/test/WebSites/ConnegWebSite/Controllers/FallbackOnTypeBasedMatchController.cs index 8e233e0ce5..773f01edcc 100644 --- a/test/WebSites/ConnegWebSite/Controllers/FallbackOnTypeBasedMatchController.cs +++ b/test/WebSites/ConnegWebSite/Controllers/FallbackOnTypeBasedMatchController.cs @@ -47,7 +47,7 @@ namespace ConnegWebsite public IActionResult OverrideTheFallback_UsingCustomFormatters(int input) { var objectResult = new ObjectResult(input); - objectResult.Formatters.Add(new StopIfNoMatchOutputFormatter()); + objectResult.Formatters.Add(new HttpNotAcceptableOutputFormatter()); objectResult.Formatters.Add(new PlainTextFormatter()); objectResult.Formatters.Add(new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), false)); return objectResult; @@ -57,7 +57,7 @@ namespace ConnegWebsite { var objectResult = new ObjectResult(input); var formattersProvider = ActionContext.HttpContext.RequestServices.GetService(); - objectResult.Formatters.Add(new StopIfNoMatchOutputFormatter()); + objectResult.Formatters.Add(new HttpNotAcceptableOutputFormatter()); foreach (var formatter in formattersProvider.OutputFormatters) { objectResult.Formatters.Add(formatter); @@ -65,26 +65,5 @@ namespace ConnegWebsite return objectResult; } - - public class StopIfNoMatchOutputFormatter : IOutputFormatter - { - // Select if no Registered content type. - public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType) - { - return contentType == null; - } - - public IReadOnlyList GetSupportedContentTypes(Type declaredType, Type runtimeType, MediaTypeHeaderValue contentType) - { - return null; - } - - public Task WriteAsync(OutputFormatterContext context) - { - var response = context.ActionContext.HttpContext.Response; - response.StatusCode = 406; - return Task.FromResult(true); - } - } } } \ No newline at end of file