diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Formatters/InputFormatter.cs b/src/Microsoft.AspNetCore.Mvc.Core/Formatters/InputFormatter.cs
index f743faec66..f231f3f1eb 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Formatters/InputFormatter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Formatters/InputFormatter.cs
@@ -3,14 +3,9 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Reflection;
-using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
-using Microsoft.AspNetCore.Mvc.Core;
-using Microsoft.Extensions.Primitives;
-using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.Formatters
{
@@ -19,33 +14,24 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
///
public abstract class InputFormatter : IInputFormatter, IApiRequestFormatMetadataProvider
{
- ///
- /// Returns UTF8 Encoding without BOM and throws on invalid bytes.
- ///
- protected static readonly Encoding UTF8EncodingWithoutBOM
- = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
-
- ///
- /// Returns UTF16 Encoding which uses littleEndian byte order with BOM and throws on invalid bytes.
- ///
- protected static readonly Encoding UTF16EncodingLittleEndian
- = new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true);
-
- ///
- /// Gets the mutable collection of character encodings supported by
- /// this . The encodings are
- /// used when reading the data.
- ///
- public IList SupportedEncodings { get; } = new List();
-
///
/// Gets the mutable collection of media type elements supported by
/// this .
///
public MediaTypeCollection SupportedMediaTypes { get; } = new MediaTypeCollection();
- protected object GetDefaultValueForType(Type modelType)
+ ///
+ /// Gets the default value for a given type. Used to return a default value when the body contains no content.
+ ///
+ /// The type of the value.
+ /// The default value for the type.
+ protected virtual object GetDefaultValueForType(Type modelType)
{
+ if (modelType == null)
+ {
+ throw new ArgumentNullException(nameof(modelType));
+ }
+
if (modelType.GetTypeInfo().IsValueType)
{
return Activator.CreateInstance(modelType);
@@ -101,6 +87,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
///
public virtual Task ReadAsync(InputFormatterContext context)
{
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
var request = context.HttpContext.Request;
if (request.ContentLength == 0)
{
@@ -117,50 +108,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// A that on completion deserializes the request body.
public abstract Task ReadRequestBodyAsync(InputFormatterContext context);
- ///
- /// Returns an based on 's
- /// character set.
- ///
- /// The .
- ///
- /// An based on 's
- /// character set. null if no supported encoding was found.
- ///
- protected Encoding SelectCharacterEncoding(InputFormatterContext context)
- {
- var request = context.HttpContext.Request;
-
- if (request.ContentType != null)
- {
- var encoding = MediaType.GetEncoding(request.ContentType);
- if (encoding != null)
- {
- foreach (var supportedEncoding in SupportedEncodings)
- {
- if (string.Equals(
- encoding.WebName,
- supportedEncoding.WebName,
- StringComparison.OrdinalIgnoreCase))
- {
- return supportedEncoding;
- }
- }
- }
- }
-
- if (SupportedEncodings.Count > 0)
- {
- return SupportedEncodings[0];
- }
-
- // No supported encoding was found so there is no way for us to start reading.
- context.ModelState.TryAddModelError(
- context.ModelName,
- Resources.FormatInputFormatterNoEncoding(GetType().FullName));
-
- return null;
- }
-
///
public IReadOnlyList GetSupportedContentTypes(string contentType, Type objectType)
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Formatters/TextInputFormatter.cs b/src/Microsoft.AspNetCore.Mvc.Core/Formatters/TextInputFormatter.cs
new file mode 100644
index 0000000000..8967511c00
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Formatters/TextInputFormatter.cs
@@ -0,0 +1,124 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Core;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+
+namespace Microsoft.AspNetCore.Mvc.Formatters
+{
+ ///
+ /// Reads an object from a request body with a text format.
+ ///
+ public abstract class TextInputFormatter : InputFormatter
+ {
+ ///
+ /// Returns UTF8 Encoding without BOM and throws on invalid bytes.
+ ///
+ protected static readonly Encoding UTF8EncodingWithoutBOM
+ = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
+
+ ///
+ /// Returns UTF16 Encoding which uses littleEndian byte order with BOM and throws on invalid bytes.
+ ///
+ protected static readonly Encoding UTF16EncodingLittleEndian
+ = new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true);
+
+ ///
+ /// Gets the mutable collection of character encodings supported by
+ /// this . The encodings are
+ /// used when reading the data.
+ ///
+ public IList SupportedEncodings { get; } = new List();
+
+ ///
+ public override Task ReadRequestBodyAsync(InputFormatterContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ var request = context.HttpContext.Request;
+ var selectedEncoding = SelectCharacterEncoding(context);
+ if (selectedEncoding == null)
+ {
+ var message = Resources.FormatUnsupportedContentType(
+ context.HttpContext.Request.ContentType);
+
+ var exception = new UnsupportedContentTypeException(message);
+ context.ModelState.AddModelError(context.ModelName, exception, context.Metadata);
+
+ return InputFormatterResult.FailureAsync();
+ }
+
+ return ReadRequestBodyAsync(context, selectedEncoding);
+ }
+
+ ///
+ /// Reads an object from the request body.
+ ///
+ /// The .
+ /// The used to read the request body.
+ /// A that on completion deserializes the request body.
+ public abstract Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding);
+
+ ///
+ /// Returns an based on 's
+ /// character set.
+ ///
+ /// The .
+ ///
+ /// An based on 's
+ /// character set. null if no supported encoding was found.
+ ///
+ protected Encoding SelectCharacterEncoding(InputFormatterContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (SupportedEncodings?.Count == 0)
+ {
+ var message = Resources.FormatTextInputFormatter_SupportedEncodingsMustNotBeEmpty(nameof(SupportedEncodings));
+ throw new InvalidOperationException(message);
+ }
+
+ var request = context.HttpContext.Request;
+
+ var requestEncoding = request.ContentType == null ? null : MediaType.GetEncoding(request.ContentType);
+ if (requestEncoding != null)
+ {
+ for (int i = 0; i < SupportedEncodings.Count; i++)
+ {
+ if (string.Equals(
+ requestEncoding.WebName,
+ SupportedEncodings[i].WebName,
+ StringComparison.OrdinalIgnoreCase))
+ {
+ return SupportedEncodings[i];
+ }
+ }
+
+ // The client specified an encoding in the content type header of the request
+ // but we don't understand it. In this situation we don't try to pick any other encoding
+ // from the list of supported encodings and read the body with that encoding.
+ // Instead, we return null and that will translate later on into a 415 Unsupported Media Type
+ // response.
+ return null;
+ }
+
+ // We want to do our best effort to read the body of the request even in the
+ // cases where the client doesn't send a content type header or sends a content
+ // type header without encoding. For that reason we pick the first encoding of the
+ // list of supported encodings and try to use that to read the body. This encoding
+ // is UTF-8 by default on our formatters, which generally is a safe choice for the
+ // encoding.
+ return SupportedEncodings[0];
+ }
+ }
+}
\ 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 65d7e0b324..74eff64d28 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Properties/Resources.Designer.cs
@@ -1098,6 +1098,22 @@ namespace Microsoft.AspNetCore.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlGeneration_ValueMustBeNumber"), p0);
}
+ ///
+ /// The list of '{0}' must not be empty. Add at least one supported encoding.
+ ///
+ internal static string TextInputFormatter_SupportedEncodingsMustNotBeEmpty
+ {
+ get { return GetString("TextInputFormatter_SupportedEncodingsMustNotBeEmpty"); }
+ }
+
+ ///
+ /// The list of '{0}' must not be empty. Add at least one supported encoding.
+ ///
+ internal static string FormatTextInputFormatter_SupportedEncodingsMustNotBeEmpty(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("TextInputFormatter_SupportedEncodingsMustNotBeEmpty"), p0);
+ }
+
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 9bb383b191..da40c89bc1 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Resources.resx
@@ -1,17 +1,17 @@
-
@@ -331,4 +331,7 @@
The field {0} must be a number.
+
+ The list of '{0}' must not be empty. Add at least one supported encoding.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonInputFormatter.cs b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonInputFormatter.cs
index 98ab34056c..45cb73608a 100644
--- a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonInputFormatter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonInputFormatter.cs
@@ -3,6 +3,7 @@
using System;
using System.Buffers;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
@@ -13,9 +14,9 @@ using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Mvc.Formatters
{
///
- /// An for JSON content.
+ /// An for JSON content.
///
- public class JsonInputFormatter : InputFormatter
+ public class JsonInputFormatter : TextInputFormatter
{
private readonly IArrayPool _charPool;
private readonly ILogger _logger;
@@ -116,22 +117,22 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
///
- public override Task ReadRequestBodyAsync(InputFormatterContext context)
+ public override Task ReadRequestBodyAsync(
+ InputFormatterContext context,
+ Encoding encoding)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
- // Get the character encoding for the content.
- var effectiveEncoding = SelectCharacterEncoding(context);
- if (effectiveEncoding == null)
+ if (encoding == null)
{
- return InputFormatterResult.FailureAsync();
+ throw new ArgumentNullException(nameof(encoding));
}
var request = context.HttpContext.Request;
- using (var streamReader = context.ReaderFactory(request.Body, effectiveEncoding))
+ using (var streamReader = context.ReaderFactory(request.Body, encoding))
{
using (var jsonReader = new JsonTextReader(streamReader))
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonPatchInputFormatter.cs b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonPatchInputFormatter.cs
index ab6c79ea7f..dde4b56291 100644
--- a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonPatchInputFormatter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonPatchInputFormatter.cs
@@ -4,6 +4,7 @@
using System;
using System.Buffers;
using System.Reflection;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
@@ -38,14 +39,21 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
///
- public async override Task ReadRequestBodyAsync(InputFormatterContext context)
+ public async override Task ReadRequestBodyAsync(
+ InputFormatterContext context,
+ Encoding encoding)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
- var result = await base.ReadRequestBodyAsync(context);
+ if (encoding == null)
+ {
+ throw new ArgumentNullException(nameof(encoding));
+ }
+
+ var result = await base.ReadRequestBodyAsync(context, encoding);
if (!result.HasError)
{
var jsonPatchDocument = (IJsonPatchDocument)result.Model;
@@ -61,6 +69,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
///
public override bool CanRead(InputFormatterContext context)
{
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
var modelTypeInfo = context.ModelType.GetTypeInfo();
if (!typeof(IJsonPatchDocument).GetTypeInfo().IsAssignableFrom(modelTypeInfo) ||
!modelTypeInfo.IsGenericType)
diff --git a/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlDataContractSerializerInputFormatter.cs b/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlDataContractSerializerInputFormatter.cs
index 50a8cb7f45..73e6ba165b 100644
--- a/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlDataContractSerializerInputFormatter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlDataContractSerializerInputFormatter.cs
@@ -12,7 +12,6 @@ using System.Xml;
using Microsoft.AspNetCore.Mvc.Formatters.Xml;
using Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal;
using Microsoft.AspNetCore.Mvc.Internal;
-using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.Formatters
{
@@ -20,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// This class handles deserialization of input XML data
/// to strongly-typed objects using .
///
- public class XmlDataContractSerializerInputFormatter : InputFormatter
+ public class XmlDataContractSerializerInputFormatter : TextInputFormatter
{
private DataContractSerializerSettings _serializerSettings;
private ConcurrentDictionary _serializerCache = new ConcurrentDictionary();
@@ -86,16 +85,20 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
///
- public override Task ReadRequestBodyAsync(InputFormatterContext context)
+ public override Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
- var effectiveEncoding = SelectCharacterEncoding(context);
- if (effectiveEncoding == null)
+ if (context == null)
{
- return InputFormatterResult.FailureAsync();
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (encoding == null)
+ {
+ throw new ArgumentNullException(nameof(encoding));
}
var request = context.HttpContext.Request;
- using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body), effectiveEncoding))
+ using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body), encoding))
{
var type = GetSerializableType(context.ModelType);
var serializer = GetCachedSerializer(type);
@@ -197,6 +200,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// The instance.
protected virtual DataContractSerializer GetCachedSerializer(Type type)
{
+ if (type == null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
object serializer;
if (!_serializerCache.TryGetValue(type, out serializer))
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlSerializerInputFormatter.cs b/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlSerializerInputFormatter.cs
index a8c65c16b0..92e1eee58b 100644
--- a/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlSerializerInputFormatter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlSerializerInputFormatter.cs
@@ -12,7 +12,6 @@ using System.Xml.Serialization;
using Microsoft.AspNetCore.Mvc.Formatters.Xml;
using Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal;
using Microsoft.AspNetCore.Mvc.Internal;
-using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.Formatters
{
@@ -20,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// This class handles deserialization of input XML data
/// to strongly-typed objects using
///
- public class XmlSerializerInputFormatter : InputFormatter
+ public class XmlSerializerInputFormatter : TextInputFormatter
{
private ConcurrentDictionary _serializerCache = new ConcurrentDictionary();
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
@@ -65,16 +64,22 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
///
- public override Task ReadRequestBodyAsync(InputFormatterContext context)
+ public override Task ReadRequestBodyAsync(
+ InputFormatterContext context,
+ Encoding encoding)
{
- var effectiveEncoding = SelectCharacterEncoding(context);
- if (effectiveEncoding == null)
+ if (context == null)
{
- return InputFormatterResult.FailureAsync();
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (encoding == null)
+ {
+ throw new ArgumentNullException(nameof(encoding));
}
var request = context.HttpContext.Request;
- using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body), effectiveEncoding))
+ using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body), encoding))
{
var type = GetSerializableType(context.ModelType);
@@ -171,6 +176,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// The instance.
protected virtual XmlSerializer GetCachedSerializer(Type type)
{
+ if (type == null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
object serializer;
if (!_serializerCache.TryGetValue(type, out serializer))
{
diff --git a/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
index b6ad84694f..eeaeabe56d 100644
--- a/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
@@ -1455,11 +1456,13 @@ namespace Microsoft.AspNetCore.Mvc.Description
public int Id { get; set; }
}
- private class MockInputFormatter : InputFormatter
+ private class MockInputFormatter : TextInputFormatter
{
public List SupportedTypes { get; } = new List();
- public override Task ReadRequestBodyAsync(InputFormatterContext context)
+ public override Task ReadRequestBodyAsync(
+ InputFormatterContext context,
+ Encoding effectiveEncoding)
{
throw new NotImplementedException();
}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/InputFormatterTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/InputFormatterTest.cs
index 09c210017f..6ddb2d3838 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/InputFormatterTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/InputFormatterTest.cs
@@ -7,7 +7,6 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
-using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.Net.Http.Headers;
using Xunit;
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/TextInputFormatterTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/TextInputFormatterTest.cs
new file mode 100644
index 0000000000..54381d6994
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Formatters/TextInputFormatterTest.cs
@@ -0,0 +1,167 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http.Internal;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.Formatters
+{
+ public class TextInputFormatterTest
+ {
+ [Fact]
+ public async Task ReadAsync_ReturnsFailure_IfItCanNotUnderstandTheContentTypeEncoding()
+ {
+ // Arrange
+ var formatter = new TestFormatter();
+ formatter.SupportedEncodings.Add(Encoding.ASCII);
+
+ var context = new InputFormatterContext(
+ new DefaultHttpContext(),
+ "something",
+ new ModelStateDictionary(),
+ new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
+ (stream, encoding) => new StreamReader(stream, encoding));
+
+ context.HttpContext.Request.ContentType = "application/json;charset=utf-8";
+ context.HttpContext.Request.ContentLength = 1;
+
+ // Act
+ var result = await formatter.ReadAsync(context);
+
+ // Assert
+ Assert.Equal(true, result.HasError);
+ Assert.Equal(true, context.ModelState.ContainsKey("something"));
+ Assert.Equal(1, context.ModelState["something"].Errors.Count);
+
+ var error = context.ModelState["something"].Errors[0];
+ Assert.IsType(error.Exception);
+ }
+
+ [Fact]
+ public void SelectCharacterEncoding_ThrowsInvalidOperationException_IfItDoesNotHaveAValidEncoding()
+ {
+ // Arrange
+ var formatter = new TestFormatter();
+
+ var context = new InputFormatterContext(
+ new DefaultHttpContext(),
+ "something",
+ new ModelStateDictionary(),
+ new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
+ (stream, encoding) => new StreamReader(stream, encoding));
+
+ context.HttpContext.Request.ContentLength = 1;
+
+ // Act & Assert
+ Assert.Throws(() => formatter.TestSelectCharacterEncoding(context));
+ }
+
+ [Fact]
+ public void SelectCharacterEncoding_ReturnsNull_IfItCanNotUnderstandContentTypeEncoding()
+ {
+ // Arrange
+ var formatter = new TestFormatter();
+ formatter.SupportedEncodings.Add(Encoding.UTF32);
+
+ var context = new InputFormatterContext(
+ new DefaultHttpContext(),
+ "something",
+ new ModelStateDictionary(),
+ new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
+ (stream, encoding) => new StreamReader(stream, encoding));
+
+ context.HttpContext.Request.ContentType = "application/json;charset=utf-8";
+
+ // Act
+ var result = formatter.TestSelectCharacterEncoding(context);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public void SelectCharacterEncoding_ReturnsContentTypeEncoding_IfItCanUnderstandIt()
+ {
+ // Arrange
+ var formatter = new TestFormatter();
+ formatter.SupportedEncodings.Add(Encoding.UTF32);
+ formatter.SupportedEncodings.Add(Encoding.UTF8);
+
+ var context = new InputFormatterContext(
+ new DefaultHttpContext(),
+ "something",
+ new ModelStateDictionary(),
+ new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
+ (stream, encoding) => new StreamReader(stream, encoding));
+
+ context.HttpContext.Request.ContentType = "application/json;charset=utf-8";
+
+ // Act
+ var result = formatter.TestSelectCharacterEncoding(context);
+
+ // Assert
+ Assert.Equal(Encoding.UTF8, result);
+ }
+
+ [Theory]
+ [InlineData("application/json")]
+ [InlineData("")]
+ public void SelectCharacterEncoding_ReturnsFirstEncoding_IfContentTypeIsNotSpecifiedOrDoesNotHaveEncoding(string contentType)
+ {
+ // Arrange
+ var formatter = new TestFormatter();
+ formatter.SupportedEncodings.Add(Encoding.UTF8);
+ formatter.SupportedEncodings.Add(Encoding.UTF32);
+
+ var context = new InputFormatterContext(
+ new DefaultHttpContext(),
+ "something",
+ new ModelStateDictionary(),
+ new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
+ (stream, encoding) => new StreamReader(stream, encoding));
+
+ context.HttpContext.Request.ContentType = contentType;
+
+ // Act
+ var result = formatter.TestSelectCharacterEncoding(context);
+
+ // Assert
+ Assert.Equal(Encoding.UTF8, result);
+ }
+
+ private class TestFormatter : TextInputFormatter
+ {
+ private readonly object _object;
+
+ public TestFormatter() : this(null) { }
+
+ public TestFormatter(object @object)
+ {
+ _object = @object;
+ }
+
+ public IList SupportedTypes { get; } = new List();
+
+ protected override bool CanReadType(Type type)
+ {
+ return SupportedTypes.Count == 0 ? true : SupportedTypes.Contains(type);
+ }
+
+ public override Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
+ {
+ return InputFormatterResult.SuccessAsync(_object);
+ }
+
+ public Encoding TestSelectCharacterEncoding(InputFormatterContext context)
+ {
+ return SelectCharacterEncoding(context);
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/BodyModelBinderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/BodyModelBinderTests.cs
index b49a518293..1b72b07940 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/BodyModelBinderTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/BodyModelBinderTests.cs
@@ -312,7 +312,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public string Name { get; set; }
}
- private class XyzFormatter : InputFormatter
+ private class XyzFormatter : TextInputFormatter
{
public XyzFormatter()
{
@@ -325,7 +325,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
return true;
}
- public override Task ReadRequestBodyAsync(InputFormatterContext context)
+ public override Task ReadRequestBodyAsync(
+ InputFormatterContext context,
+ Encoding effectiveEncoding)
{
throw new InvalidOperationException("Your input is bad!");
}
diff --git a/test/WebSites/FormatterWebSite/StringInputFormatter.cs b/test/WebSites/FormatterWebSite/StringInputFormatter.cs
index c93abffdca..dc80e3dc62 100644
--- a/test/WebSites/FormatterWebSite/StringInputFormatter.cs
+++ b/test/WebSites/FormatterWebSite/StringInputFormatter.cs
@@ -10,7 +10,7 @@ using Microsoft.Net.Http.Headers;
namespace FormatterWebSite
{
- public class StringInputFormatter : InputFormatter
+ public class StringInputFormatter : TextInputFormatter
{
public StringInputFormatter()
{
@@ -20,14 +20,8 @@ namespace FormatterWebSite
SupportedEncodings.Add(Encoding.Unicode);
}
- public override Task ReadRequestBodyAsync(InputFormatterContext context)
+ public override Task ReadRequestBodyAsync(InputFormatterContext context, Encoding effectiveEncoding)
{
- var effectiveEncoding = SelectCharacterEncoding(context);
- if (effectiveEncoding == null)
- {
- return InputFormatterResult.FailureAsync();
- }
-
var request = context.HttpContext.Request;
using (var reader = new StreamReader(request.Body, effectiveEncoding))
{