[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.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
|
|||
/// <inheritdoc />
|
||||
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
|
|||
/// <inheritdoc />
|
||||
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))
|
||||
{
|
||||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
|||
/// </summary>
|
||||
public abstract class OutputFormatter : IOutputFormatter, IApiResponseFormatMetadataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OutputFormatter"/> class.
|
||||
/// </summary>
|
||||
protected OutputFormatter()
|
||||
{
|
||||
SupportedMediaTypes = new MediaTypeCollection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mutable collection of media type elements supported by
|
||||
/// this <see cref="OutputFormatter"/>.
|
||||
/// </summary>
|
||||
public MediaTypeCollection SupportedMediaTypes { get; }
|
||||
public MediaTypeCollection SupportedMediaTypes { get; } = new MediaTypeCollection();
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
|||
/// <returns>A task which can write the response body.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</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>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
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
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
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
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
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
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
|
@ -340,4 +340,7 @@
|
|||
<data name="TextOutpurFormatter_WriteResponseBodyAsynNotSupported" xml:space="preserve">
|
||||
<value>'{0}' is not supported by '{1}'. Use '{2}' instead.</value>
|
||||
</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>
|
||||
|
|
@ -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<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
|
||||
{
|
||||
public IList<Type> SupportedTypes { get; } = new List<Type>();
|
||||
|
|
|
|||
|
|
@ -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<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
|
||||
{
|
||||
public TestOutputFormatter()
|
||||
|
|
|
|||
Loading…
Reference in New Issue