[Fixes #4050] Throw an exception on CanRead/Write and GetSupportedContentTypes when the list of media types is empty

This commit is contained in:
jacalvar 2016-02-05 19:33:31 -08:00
parent 7337f50112
commit 910f0139f9
6 changed files with 156 additions and 66 deletions

View File

@ -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
{ {

View File

@ -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
{ {

View File

@ -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);

View File

@ -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>

View File

@ -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>();

View File

@ -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()