[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
|
||||||
{
|
{
|
||||||
|
|
@ -160,4 +164,4 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// <returns>A task which can write the response body.</returns>
|
/// <returns>A task which can write the response body.</returns>
|
||||||
public abstract Task WriteResponseBodyAsync(OutputFormatterWriteContext context);
|
public abstract Task WriteResponseBodyAsync(OutputFormatterWriteContext context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -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