diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Formatters/InputFormatter.cs b/src/Microsoft.AspNetCore.Mvc.Core/Formatters/InputFormatter.cs index 8b9c00115f..c6fe0e94a1 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Formatters/InputFormatter.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Formatters/InputFormatter.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Core; namespace Microsoft.AspNetCore.Mvc.Formatters { @@ -43,6 +44,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters /// public virtual bool CanRead(InputFormatterContext context) { + if (SupportedMediaTypes.Count == 0) + { + var message = Resources.FormatFormatter_NoMediaTypes( + GetType().FullName, + nameof(SupportedMediaTypes)); + + throw new InvalidOperationException(message); + } + if (!CanReadType(context.ModelType)) { return false; @@ -111,6 +121,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters /// public virtual IReadOnlyList GetSupportedContentTypes(string contentType, Type objectType) { + if (SupportedMediaTypes.Count == 0) + { + var message = Resources.FormatFormatter_NoMediaTypes( + GetType().FullName, + nameof(SupportedMediaTypes)); + + throw new InvalidOperationException(message); + } + if (!CanReadType(objectType)) { return null; @@ -119,7 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters if (contentType == null) { // If contentType is null, then any type we support is valid. - return SupportedMediaTypes.Count > 0 ? SupportedMediaTypes : null; + return SupportedMediaTypes; } else { diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Formatters/OutputFormatter.cs b/src/Microsoft.AspNetCore.Mvc.Core/Formatters/OutputFormatter.cs index 314fadbfe9..9ba3d68b3e 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Formatters/OutputFormatter.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Formatters/OutputFormatter.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Core; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Mvc.Formatters @@ -14,19 +15,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters /// public abstract class OutputFormatter : IOutputFormatter, IApiResponseFormatMetadataProvider { - /// - /// Initializes a new instance of the class. - /// - protected OutputFormatter() - { - SupportedMediaTypes = new MediaTypeCollection(); - } - /// /// Gets the mutable collection of media type elements supported by /// this . /// - public MediaTypeCollection SupportedMediaTypes { get; } + public MediaTypeCollection SupportedMediaTypes { get; } = new MediaTypeCollection(); /// /// Returns a value indicating whether or not the given type can be written by this serializer. @@ -43,6 +36,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters string contentType, Type objectType) { + if (SupportedMediaTypes.Count == 0) + { + var message = Resources.FormatFormatter_NoMediaTypes( + GetType().FullName, + nameof(SupportedMediaTypes)); + + throw new InvalidOperationException(message); + } + if (!CanWriteType(objectType)) { return null; @@ -51,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters if (contentType == null) { // If contentType is null, then any type we support is valid. - return SupportedMediaTypes.Count > 0 ? SupportedMediaTypes : null; + return SupportedMediaTypes; } else { @@ -87,6 +89,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters throw new ArgumentNullException(nameof(context)); } + if (SupportedMediaTypes.Count == 0) + { + var message = Resources.FormatFormatter_NoMediaTypes( + GetType().FullName, + nameof(SupportedMediaTypes)); + + throw new InvalidOperationException(message); + } + if (!CanWriteType(context.ObjectType)) { return false; @@ -96,15 +107,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters { // If the desired content type is set to null, then the current formatter can write anything // it wants. - if (SupportedMediaTypes.Count > 0) - { - context.ContentType = new StringSegment(SupportedMediaTypes[0]); - return true; - } - else - { - return false; - } + context.ContentType = new StringSegment(SupportedMediaTypes[0]); + return true; } else { @@ -160,4 +164,4 @@ namespace Microsoft.AspNetCore.Mvc.Formatters /// A task which can write the response body. public abstract Task WriteResponseBodyAsync(OutputFormatterWriteContext context); } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Mvc.Core/Properties/Resources.Designer.cs index db8b79a02b..55a474f187 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Properties/Resources.Designer.cs @@ -1146,6 +1146,22 @@ namespace Microsoft.AspNetCore.Mvc.Core return string.Format(CultureInfo.CurrentCulture, GetString("TextOutpurFormatter_WriteResponseBodyAsynNotSupported"), p0, p1, p2); } + /// + /// No media types found in '{0}.{1}'. Add at least one media type to the list of supported media types. + /// + internal static string Formatter_NoMediaTypes + { + get { return GetString("Formatter_NoMediaTypes"); } + } + + /// + /// No media types found in '{0}.{1}'. Add at least one media type to the list of supported media types. + /// + internal static string FormatFormatter_NoMediaTypes(object p0, object p1) + { + return string.Format(CultureInfo.CurrentCulture, GetString("Formatter_NoMediaTypes"), p0, p1); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx b/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx index 84d1befc53..04dc1391b1 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx +++ b/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx @@ -1,17 +1,17 @@  - @@ -340,4 +340,7 @@ '{0}' is not supported by '{1}'. Use '{2}' instead. + + No media types found in '{0}.{1}'. Add at least one media type to the list of supported media types. + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/InputFormatterTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/InputFormatterTest.cs index 6ddb2d3838..eae5c6eecd 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/InputFormatterTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/InputFormatterTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http.Internal; @@ -382,6 +383,41 @@ namespace Microsoft.AspNetCore.Mvc.Formatters Assert.Collection(results, c => Assert.Equal("text/xml", c)); } + [Fact] + public void CanRead_ThrowsInvalidOperationException_IfMediaTypesListIsEmpty() + { + // Arrange + var formatter = new BadConfigurationFormatter(); + var context = new InputFormatterContext( + new DefaultHttpContext(), + "", + new ModelStateDictionary(), + new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)), + (s, e) => new StreamReader(s, e)); + + // Act & Assert + Assert.Throws(() => formatter.CanRead(context)); + } + + [Fact] + public void GetSupportedContentTypes_ThrowsInvalidOperationException_IfMediaTypesListIsEmpty() + { + // Arrange + var formatter = new BadConfigurationFormatter(); + + // Act & Assert + Assert.Throws( + () => formatter.GetSupportedContentTypes("application/json", typeof(object))); + } + + private class BadConfigurationFormatter : InputFormatter + { + public override Task ReadRequestBodyAsync(InputFormatterContext context) + { + throw new NotImplementedException(); + } + } + private class TestFormatter : InputFormatter { public IList SupportedTypes { get; } = new List(); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/OutputFormatterTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/OutputFormatterTests.cs index 6476947fd5..20407b07fe 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/OutputFormatterTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/OutputFormatterTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -138,24 +139,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters Assert.Null(contentTypes); } - [Fact] - public void GetSupportedContentTypes_ReturnsAllContentTypes_ReturnsNullWithNoSupportedContentTypes() - { - // Arrange - var formatter = new TestOutputFormatter(); - - // Intentionally empty - formatter.SupportedMediaTypes.Clear(); - - // Act - var contentTypes = formatter.GetSupportedContentTypes( - contentType: null, - objectType: typeof(int)); - - // Assert - Assert.Null(contentTypes); - } - private class TypeSpecificFormatter : OutputFormatter { public List SupportedTypes { get; } = new List(); @@ -171,6 +154,35 @@ namespace Microsoft.AspNetCore.Mvc.Formatters } } + [Fact] + public void CanWrite_ThrowsInvalidOperationException_IfMediaTypesListIsEmpty() + { + // Arrange + var formatter = new TestOutputFormatter(); + formatter.SupportedMediaTypes.Clear(); + + var context = new OutputFormatterWriteContext( + new DefaultHttpContext(), + (s, e) => new StreamWriter(s, e), + typeof(object), + new object()); + + // Act & Assert + Assert.Throws(() => formatter.CanWriteResult(context)); + } + + [Fact] + public void GetSupportedContentTypes_ThrowsInvalidOperationException_IfMediaTypesListIsEmpty() + { + // Arrange + var formatter = new TestOutputFormatter(); + formatter.SupportedMediaTypes.Clear(); + + // Act & Assert + Assert.Throws( + () => formatter.GetSupportedContentTypes("application/json", typeof(object))); + } + private class TestOutputFormatter : OutputFormatter { public TestOutputFormatter()