[Fixes #4050] Throw an exception on CanRead/Write and GetSupportedContentTypes when the list of media types is empty
This commit is contained in:
parent
7337f50112
commit
910f0139f9
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Core;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Formatters
|
namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
{
|
{
|
||||||
|
|
@ -43,6 +44,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual bool CanRead(InputFormatterContext context)
|
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))
|
if (!CanReadType(context.ModelType))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -111,6 +121,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual IReadOnlyList<string> GetSupportedContentTypes(string contentType, Type objectType)
|
public virtual IReadOnlyList<string> 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))
|
if (!CanReadType(objectType))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -119,7 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
if (contentType == null)
|
if (contentType == null)
|
||||||
{
|
{
|
||||||
// If contentType is null, then any type we support is valid.
|
// If contentType is null, then any type we support is valid.
|
||||||
return SupportedMediaTypes.Count > 0 ? SupportedMediaTypes : null;
|
return SupportedMediaTypes;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Core;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Formatters
|
namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
|
|
@ -14,19 +15,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class OutputFormatter : IOutputFormatter, IApiResponseFormatMetadataProvider
|
public abstract class OutputFormatter : IOutputFormatter, IApiResponseFormatMetadataProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="OutputFormatter"/> class.
|
|
||||||
/// </summary>
|
|
||||||
protected OutputFormatter()
|
|
||||||
{
|
|
||||||
SupportedMediaTypes = new MediaTypeCollection();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the mutable collection of media type elements supported by
|
/// Gets the mutable collection of media type elements supported by
|
||||||
/// this <see cref="OutputFormatter"/>.
|
/// this <see cref="OutputFormatter"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MediaTypeCollection SupportedMediaTypes { get; }
|
public MediaTypeCollection SupportedMediaTypes { get; } = new MediaTypeCollection();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether or not the given type can be written by this serializer.
|
/// 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,
|
string contentType,
|
||||||
Type objectType)
|
Type objectType)
|
||||||
{
|
{
|
||||||
|
if (SupportedMediaTypes.Count == 0)
|
||||||
|
{
|
||||||
|
var message = Resources.FormatFormatter_NoMediaTypes(
|
||||||
|
GetType().FullName,
|
||||||
|
nameof(SupportedMediaTypes));
|
||||||
|
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
if (!CanWriteType(objectType))
|
if (!CanWriteType(objectType))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -51,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
if (contentType == null)
|
if (contentType == null)
|
||||||
{
|
{
|
||||||
// If contentType is null, then any type we support is valid.
|
// If contentType is null, then any type we support is valid.
|
||||||
return SupportedMediaTypes.Count > 0 ? SupportedMediaTypes : null;
|
return SupportedMediaTypes;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -87,6 +89,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
throw new ArgumentNullException(nameof(context));
|
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))
|
if (!CanWriteType(context.ObjectType))
|
||||||
{
|
{
|
||||||
return false;
|
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
|
// If the desired content type is set to null, then the current formatter can write anything
|
||||||
// it wants.
|
// it wants.
|
||||||
if (SupportedMediaTypes.Count > 0)
|
context.ContentType = new StringSegment(SupportedMediaTypes[0]);
|
||||||
{
|
return true;
|
||||||
context.ContentType = new StringSegment(SupportedMediaTypes[0]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1146,6 +1146,22 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TextOutpurFormatter_WriteResponseBodyAsynNotSupported"), p0, p1, p2);
|
return string.Format(CultureInfo.CurrentCulture, GetString("TextOutpurFormatter_WriteResponseBodyAsynNotSupported"), p0, p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No media types found in '{0}.{1}'. Add at least one media type to the list of supported media types.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Formatter_NoMediaTypes
|
||||||
|
{
|
||||||
|
get { return GetString("Formatter_NoMediaTypes"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No media types found in '{0}.{1}'. Add at least one media type to the list of supported media types.
|
||||||
|
/// </summary>
|
||||||
|
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)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -340,4 +340,7 @@
|
||||||
<data name="TextOutpurFormatter_WriteResponseBodyAsynNotSupported" xml:space="preserve">
|
<data name="TextOutpurFormatter_WriteResponseBodyAsynNotSupported" xml:space="preserve">
|
||||||
<value>'{0}' is not supported by '{1}'. Use '{2}' instead.</value>
|
<value>'{0}' is not supported by '{1}'. Use '{2}' instead.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Formatter_NoMediaTypes" xml:space="preserve">
|
||||||
|
<value>No media types found in '{0}.{1}'. Add at least one media type to the list of supported media types.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http.Internal;
|
using Microsoft.AspNetCore.Http.Internal;
|
||||||
|
|
@ -382,6 +383,41 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
Assert.Collection(results, c => Assert.Equal("text/xml", c));
|
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<InvalidOperationException>(() => formatter.CanRead(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetSupportedContentTypes_ThrowsInvalidOperationException_IfMediaTypesListIsEmpty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formatter = new BadConfigurationFormatter();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(
|
||||||
|
() => formatter.GetSupportedContentTypes("application/json", typeof(object)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BadConfigurationFormatter : InputFormatter
|
||||||
|
{
|
||||||
|
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class TestFormatter : InputFormatter
|
private class TestFormatter : InputFormatter
|
||||||
{
|
{
|
||||||
public IList<Type> SupportedTypes { get; } = new List<Type>();
|
public IList<Type> SupportedTypes { get; } = new List<Type>();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
@ -138,24 +139,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
Assert.Null(contentTypes);
|
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
|
private class TypeSpecificFormatter : OutputFormatter
|
||||||
{
|
{
|
||||||
public List<Type> SupportedTypes { get; } = new List<Type>();
|
public List<Type> SupportedTypes { get; } = new List<Type>();
|
||||||
|
|
@ -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<InvalidOperationException>(() => formatter.CanWriteResult(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetSupportedContentTypes_ThrowsInvalidOperationException_IfMediaTypesListIsEmpty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formatter = new TestOutputFormatter();
|
||||||
|
formatter.SupportedMediaTypes.Clear();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(
|
||||||
|
() => formatter.GetSupportedContentTypes("application/json", typeof(object)));
|
||||||
|
}
|
||||||
|
|
||||||
private class TestOutputFormatter : OutputFormatter
|
private class TestOutputFormatter : OutputFormatter
|
||||||
{
|
{
|
||||||
public TestOutputFormatter()
|
public TestOutputFormatter()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue