Fix Xml formatters to taking in MvcOptions to take affect of options mutation

Related to issue [Fixes #6858] Changes to MvcOption's settings (SuppressInputFormatterBuffering & AllowBindingUndefinedValueToEnumType) are not taking affect
This commit is contained in:
Kiran Challa 2017-12-04 17:04:02 -08:00
parent e09ea40551
commit db38da7edb
8 changed files with 396 additions and 89 deletions

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
options.ModelMetadataDetailsProviders.Add(new DataMemberRequiredBindingMetadataProvider());
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter(options.SuppressInputFormatterBuffering));
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter(options));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider("System.Xml.Linq.XObject"));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider("System.Xml.XmlNode"));

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
public void Configure(MvcOptions options)
{
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
options.InputFormatters.Add(new XmlSerializerInputFormatter(options.SuppressInputFormatterBuffering));
options.InputFormatters.Add(new XmlSerializerInputFormatter(options));
}
}
}

View File

@ -28,24 +28,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
private readonly ConcurrentDictionary<Type, object> _serializerCache = new ConcurrentDictionary<Type, object>();
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
private readonly bool _suppressInputFormatterBuffering;
private readonly MvcOptions _options;
private DataContractSerializerSettings _serializerSettings;
/// <summary>
/// Initializes a new instance of DataContractSerializerInputFormatter
/// Initializes a new instance of <see cref="XmlDataContractSerializerInputFormatter"/>.
/// </summary>
public XmlDataContractSerializerInputFormatter() :
this(suppressInputFormatterBuffering: false)
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
public XmlDataContractSerializerInputFormatter()
{
}
/// <summary>
/// Initializes a new instance of DataContractSerializerInputFormatter
/// </summary>
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</param>
public XmlDataContractSerializerInputFormatter(bool suppressInputFormatterBuffering)
{
_suppressInputFormatterBuffering = suppressInputFormatterBuffering;
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
SupportedEncodings.Add(UTF16EncodingLittleEndian);
@ -59,6 +50,29 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
/// <summary>
/// Initializes a new instance of <see cref="XmlDataContractSerializerInputFormatter"/>.
/// </summary>
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</param>
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
public XmlDataContractSerializerInputFormatter(bool suppressInputFormatterBuffering)
: this()
{
_suppressInputFormatterBuffering = suppressInputFormatterBuffering;
}
/// <summary>
/// Initializes a new instance of <see cref="XmlDataContractSerializerInputFormatter"/>.
/// </summary>
/// <param name="options">The <see cref="MvcOptions"/>.</param>
public XmlDataContractSerializerInputFormatter(MvcOptions options)
#pragma warning disable CS0618
: this()
#pragma warning restore CS0618
{
_options = options;
}
/// <summary>
/// Gets the list of <see cref="IWrapperProviderFactory"/> to
/// provide the wrapping type for de-serialization.
@ -126,7 +140,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
var request = context.HttpContext.Request;
if (!request.Body.CanSeek && !_suppressInputFormatterBuffering)
var suppressInputFormatterBuffering = _options?.SuppressInputFormatterBuffering ?? _suppressInputFormatterBuffering;
if (!request.Body.CanSeek && !suppressInputFormatterBuffering)
{
// XmlDataContractSerializer does synchronous reads. In order to avoid blocking on the stream, we asynchronously
// read everything into a buffer, and then seek back to the beginning.

View File

@ -28,23 +28,14 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
private readonly ConcurrentDictionary<Type, object> _serializerCache = new ConcurrentDictionary<Type, object>();
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
private readonly bool _suppressInputFormatterBuffering;
private readonly MvcOptions _options;
/// <summary>
/// Initializes a new instance of XmlSerializerInputFormatter.
/// </summary>
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
public XmlSerializerInputFormatter()
: this(suppressInputFormatterBuffering: false)
{
}
/// <summary>
/// Initializes a new instance of XmlSerializerInputFormatter.
/// </summary>
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</param>
public XmlSerializerInputFormatter(bool suppressInputFormatterBuffering)
{
_suppressInputFormatterBuffering = suppressInputFormatterBuffering;
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
SupportedEncodings.Add(UTF16EncodingLittleEndian);
@ -56,6 +47,29 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
/// <summary>
/// Initializes a new instance of <see cref="XmlSerializerInputFormatter"/>.
/// </summary>
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</param>
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
public XmlSerializerInputFormatter(bool suppressInputFormatterBuffering)
: this()
{
_suppressInputFormatterBuffering = suppressInputFormatterBuffering;
}
/// <summary>
/// Initializes a new instance of <see cref="XmlSerializerInputFormatter"/>.
/// </summary>
/// <param name="options">The <see cref="MvcOptions"/>.</param>
public XmlSerializerInputFormatter(MvcOptions options)
#pragma warning disable CS0618
: this()
#pragma warning restore CS0618
{
_options = options;
}
/// <summary>
/// Gets the list of <see cref="IWrapperProviderFactory"/> to
/// provide the wrapping type for de-serialization.
@ -107,7 +121,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
var request = context.HttpContext.Request;
if (!request.Body.CanSeek && !_suppressInputFormatterBuffering)
var suppressInputFormatterBuffering = _options?.SuppressInputFormatterBuffering ?? _suppressInputFormatterBuffering;
if (!request.Body.CanSeek && !suppressInputFormatterBuffering)
{
// XmlSerializer does synchronous reads. In order to avoid blocking on the stream, we asynchronously
// read everything into a buffer, and then seek back to the beginning.

View File

@ -243,10 +243,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{
return new TheoryData<IInputFormatter, InputFormatterExceptionModelStatePolicy>()
{
{ new XmlSerializerInputFormatter(), InputFormatterExceptionModelStatePolicy.AllExceptions },
{ new XmlSerializerInputFormatter(), InputFormatterExceptionModelStatePolicy.MalformedInputExceptions },
{ new XmlDataContractSerializerInputFormatter(), InputFormatterExceptionModelStatePolicy.AllExceptions },
{ new XmlDataContractSerializerInputFormatter(), InputFormatterExceptionModelStatePolicy.MalformedInputExceptions },
{ new XmlSerializerInputFormatter(new MvcOptions()), InputFormatterExceptionModelStatePolicy.AllExceptions },
{ new XmlSerializerInputFormatter(new MvcOptions()), InputFormatterExceptionModelStatePolicy.MalformedInputExceptions },
{ new XmlDataContractSerializerInputFormatter(new MvcOptions()), InputFormatterExceptionModelStatePolicy.AllExceptions },
{ new XmlDataContractSerializerInputFormatter(new MvcOptions()), InputFormatterExceptionModelStatePolicy.MalformedInputExceptions },
};
}
}
@ -821,6 +821,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
private bool _throwNonInputFormatterException;
public TestableXmlSerializerInputFormatter(bool throwNonInputFormatterException)
: base(new MvcOptions())
{
_throwNonInputFormatterException = throwNonInputFormatterException;
}
@ -842,6 +843,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
private bool _throwNonInputFormatterException;
public TestableXmlDataContractSerializerInputFormatter(bool throwNonInputFormatterException)
: base(new MvcOptions())
{
_throwNonInputFormatterException = throwNonInputFormatterException;
}
@ -885,6 +887,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
private bool _throwNonInputFormatterException;
public DerivedXmlSerializerInputFormatter(bool throwNonInputFormatterException)
: base(new MvcOptions())
{
_throwNonInputFormatterException = throwNonInputFormatterException;
}
@ -906,6 +909,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
private bool _throwNonInputFormatterException;
public DerivedXmlDataContractSerializerInputFormatter(bool throwNonInputFormatterException)
: base(new MvcOptions())
{
_throwNonInputFormatterException = throwNonInputFormatterException;
}

View File

@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead)
{
// Arrange
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes("content");
var modelState = new ModelStateDictionary();
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void HasProperSuppportedMediaTypes()
{
// Arrange & Act
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
// Assert
Assert.Contains("application/xml", formatter.SupportedMediaTypes
@ -127,7 +127,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void HasProperSuppportedEncodings()
{
// Arrange & Act
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
// Assert
Assert.Contains(formatter.SupportedEncodings, i => i.WebName == "utf-8");
@ -142,10 +142,13 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var expectedString = "TestString";
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
#pragma warning disable CS0618
var formatter = new XmlDataContractSerializerInputFormatter();
#pragma warning restore CS0618
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
@ -186,10 +189,92 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var expectedString = "TestString";
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
#pragma warning disable CS0618
var formatter = new XmlDataContractSerializerInputFormatter(suppressInputFormatterBuffering: true);
#pragma warning restore CS0618
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
httpContext.Request.ContentType = "application/xml";
var context = GetInputFormatterContext(httpContext, typeof(TestLevelOne));
// Act
var result = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(result);
Assert.False(result.HasError);
var model = Assert.IsType<TestLevelOne>(result.Model);
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
// Reading again should fail as buffering request body is disabled
await Assert.ThrowsAsync<XmlException>(() => formatter.ReadAsync(context));
}
[Fact]
public async Task BuffersRequestBody_ByDefaultUsingMvcOptions()
{
// Arrange
var expectedInt = 10;
var expectedString = "TestString";
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
httpContext.Request.ContentType = "application/json";
var context = GetInputFormatterContext(httpContext, typeof(TestLevelOne));
// Act
var result = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(result);
Assert.False(result.HasError);
var model = Assert.IsType<TestLevelOne>(result.Model);
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
Assert.True(httpContext.Request.Body.CanSeek);
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
result = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(result);
Assert.False(result.HasError);
model = Assert.IsType<TestLevelOne>(result.Model);
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
}
[Fact]
public async Task SuppressInputFormatterBufferingSetToTrue_DoesNotBufferRequestBody_UsingMvcOptions()
{
// Arrange
var expectedInt = 10;
var expectedString = "TestString";
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
var formatter = new XmlDataContractSerializerInputFormatter(
new MvcOptions() { SuppressInputFormatterBuffering = true });
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
@ -220,10 +305,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var expectedString = "TestString";
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne));
@ -239,6 +324,43 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
Assert.Equal(expectedString, model.sampleString);
}
[Fact]
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions()
{
// Arrange
var expectedInt = 10;
var expectedString = "TestString";
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
var mvcOptions = new MvcOptions() { SuppressInputFormatterBuffering = false };
var formatter = new XmlDataContractSerializerInputFormatter(mvcOptions);
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
httpContext.Request.ContentType = "application/xml";
var context = GetInputFormatterContext(httpContext, typeof(TestLevelOne));
// Act
// Mutate options after passing into the constructor to make sure that the value type is not store in the constructor
mvcOptions.SuppressInputFormatterBuffering = true;
var result = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(result);
Assert.False(result.HasError);
var model = Assert.IsType<TestLevelOne>(result.Model);
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
// Reading again should fail as buffering request body is disabled
await Assert.ThrowsAsync<XmlException>(() => formatter.ReadAsync(context));
}
[Fact]
public async Task ReadAsync_ReadsComplexTypes()
{
@ -248,11 +370,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var expectedLevelTwoString = "102";
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelTwo><SampleString>" + expectedLevelTwoString + "</SampleString>" +
"<TestOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestOne></TestLevelTwo>";
"<TestLevelTwo><SampleString>" + expectedLevelTwoString + "</SampleString>" +
"<TestOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestOne></TestLevelTwo>";
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
@ -277,7 +399,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass><SampleInt>" + expectedInt + "</SampleInt></DummyClass>";
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
formatter.MaxDepth = 10;
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
@ -298,10 +420,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
{
// Arrange
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelTwo><SampleString>test</SampleString>" +
"<TestOne><SampleInt>10</SampleInt>" +
"<sampleString>test</sampleString></TestOne></TestLevelTwo>";
var formatter = new XmlDataContractSerializerInputFormatter();
"<TestLevelTwo><SampleString>test</SampleString>" +
"<TestOne><SampleInt>10</SampleInt><sampleString>test</sampleString></TestOne></TestLevelTwo>";
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
formatter.MaxDepth = 1;
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
@ -315,10 +436,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
{
// Arrange
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelTwo><SampleString>test</SampleString>" +
"<TestOne><SampleInt>10</SampleInt>" +
"<sampleString>test</sampleString></TestOne></TestLevelTwo>";
var formatter = new XmlDataContractSerializerInputFormatter();
"<TestLevelTwo><SampleString>test</SampleString><TestOne><SampleInt>10</SampleInt>" +
"<sampleString>test</sampleString></TestOne></TestLevelTwo>";
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
formatter.XmlDictionaryReaderQuotas.MaxStringContentLength = 2;
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
@ -331,7 +451,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void SetMaxDepth_ThrowsWhenMaxDepthIsBelowOne()
{
// Arrange
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
// Act & Assert
Assert.Throws<ArgumentException>(() => formatter.MaxDepth = 0);
@ -343,7 +463,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
// Arrange
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass><SampleInt>10</SampleInt></DummyClass>";
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
@ -372,7 +492,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
Buffer.BlockCopy(inp, 0, contentBytes, inpStart.Length, inp.Length);
Buffer.BlockCopy(inpEnd, 0, contentBytes, inpStart.Length + inp.Length, inpEnd.Length);
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
// Act
@ -390,7 +510,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var inputBytes = Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass><SampleInt>1000</SampleInt></DummyClass>");
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var modelState = new ModelStateDictionary();
var httpContext = GetHttpContext(inputBytes, contentType: "application/xml; charset=utf-16");
@ -427,7 +547,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
Buffer.BlockCopy(bom, 0, contentBytes, inputStart.Length, bom.Length);
Buffer.BlockCopy(inputEnd, 0, contentBytes, inputStart.Length + bom.Length, inputEnd.Length);
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
// Act
@ -453,7 +573,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString></TestLevelOne>";
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.Unicode.GetBytes(input);
var modelState = new ModelStateDictionary();
@ -491,7 +611,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
"<{0} xmlns=\"{1}\"><SampleInt xmlns=\"\">1</SampleInt></{0}>",
SubstituteRootName,
SubstituteRootNamespace);
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
@ -519,7 +639,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
RootName = dictionary.Add(SubstituteRootName),
RootNamespace = dictionary.Add(SubstituteRootNamespace)
};
var formatter = new XmlDataContractSerializerInputFormatter
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions())
{
SerializerSettings = settings
};
@ -548,7 +668,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
+ "<SampleString>Some text</SampleString></DummyClass>",
KnownTypeName,
InstanceNamespace);
var formatter = new XmlDataContractSerializerInputFormatter();
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
@ -576,7 +696,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
{
KnownTypes = new[] { typeof(SomeDummyClass) }
};
var formatter = new XmlDataContractSerializerInputFormatter
var formatter = new XmlDataContractSerializerInputFormatter(new MvcOptions())
{
SerializerSettings = settings
};
@ -632,6 +752,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
{
public int createSerializerCalledCount = 0;
public TestXmlDataContractSerializerInputFormatter()
: base(new MvcOptions())
{
}
protected override DataContractSerializer CreateSerializer(Type type)
{
createSerializerCalledCount++;

View File

@ -49,11 +49,108 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var expectedDateTime = XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc);
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
#pragma warning disable CS0618
var formatter = new XmlSerializerInputFormatter();
#pragma warning restore CS0618
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
httpContext.Request.ContentType = "application/json";
var context = GetInputFormatterContext(httpContext, typeof(TestLevelOne));
// Act
var result = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(result);
Assert.False(result.HasError);
var model = Assert.IsType<TestLevelOne>(result.Model);
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
Assert.Equal(
XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc),
model.SampleDate);
Assert.True(httpContext.Request.Body.CanSeek);
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
result = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(result);
Assert.False(result.HasError);
model = Assert.IsType<TestLevelOne>(result.Model);
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
Assert.Equal(
XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc),
model.SampleDate);
}
[Fact]
public async Task SuppressInputFormatterBufferingSetToTrue_DoesNotBufferRequestBody_ObsoleteParameter()
{
// Arrange
var expectedInt = 10;
var expectedString = "TestString";
var expectedDateTime = XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc);
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
#pragma warning disable CS0618
var formatter = new XmlSerializerInputFormatter(suppressInputFormatterBuffering: true);
#pragma warning restore CS0618
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
httpContext.Request.ContentType = "application/xml";
var context = GetInputFormatterContext(httpContext, typeof(TestLevelOne));
// Act
var result = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(result);
Assert.False(result.HasError);
var model = Assert.IsType<TestLevelOne>(result.Model);
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
Assert.Equal(
XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc),
model.SampleDate);
// Reading again should fail as buffering request body is disabled
await Assert.ThrowsAsync<XmlException>(() => formatter.ReadAsync(context));
}
[Fact]
public async Task BuffersRequestBody_ByDefaultUsingMvcOptions()
{
// Arrange
var expectedInt = 10;
var expectedString = "TestString";
var expectedDateTime = XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc);
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
@ -101,11 +198,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var expectedDateTime = XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc);
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
var formatter = new XmlSerializerInputFormatter(suppressInputFormatterBuffering: true);
var formatter = new XmlSerializerInputFormatter(new MvcOptions() { SuppressInputFormatterBuffering = true });
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
@ -131,6 +228,50 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
await Assert.ThrowsAsync<XmlException>(() => formatter.ReadAsync(context));
}
[Fact]
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions()
{
// Arrange
var expectedInt = 10;
var expectedString = "TestString";
var expectedDateTime = XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc);
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<TestLevelOne><SampleInt>" + expectedInt + "</SampleInt>" +
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
var mvcOptions = new MvcOptions();
mvcOptions.SuppressInputFormatterBuffering = false;
var formatter = new XmlSerializerInputFormatter(mvcOptions);
var contentBytes = Encoding.UTF8.GetBytes(input);
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
httpContext.Request.ContentType = "application/xml";
var context = GetInputFormatterContext(httpContext, typeof(TestLevelOne));
// Act
// Mutate options after passing into the constructor to make sure that the value type is not store in the constructor
mvcOptions.SuppressInputFormatterBuffering = true;
var result = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(result);
Assert.False(result.HasError);
var model = Assert.IsType<TestLevelOne>(result.Model);
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
Assert.Equal(
XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc),
model.SampleDate);
// Reading again should fail as buffering request body is disabled
await Assert.ThrowsAsync<XmlException>(() => formatter.ReadAsync(context));
}
[Theory]
[InlineData("application/xml", true)]
[InlineData("application/*", false)]
@ -150,7 +291,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead)
{
// Arrange
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes("content");
var modelState = new ModelStateDictionary();
@ -178,7 +319,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void CanRead_ReturnsFalse_ForAnyUnsupportedModelType(Type modelType, bool expectedCanRead)
{
// Arrange
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes("content");
var context = GetInputFormatterContext(contentBytes, modelType);
@ -212,7 +353,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void HasProperSuppportedMediaTypes()
{
// Arrange & Act
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
// Assert
Assert.Contains("application/xml", formatter.SupportedMediaTypes
@ -225,7 +366,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void HasProperSuppportedEncodings()
{
// Arrange & Act
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
// Assert
Assert.Contains(formatter.SupportedEncodings, i => i.WebName == "utf-8");
@ -245,7 +386,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne));
@ -279,7 +420,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestOne></TestLevelTwo>";
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
@ -307,7 +448,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass><SampleInt>" + expectedInt + "</SampleInt></DummyClass>";
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
formatter.MaxDepth = 10;
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
@ -334,7 +475,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
"<sampleString>test</sampleString>" +
"<SampleDate>" + XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc)
+ "</SampleDate></TestOne></TestLevelTwo>";
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
formatter.MaxDepth = 1;
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
@ -355,7 +496,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
"<sampleString>test</sampleString>" +
"<SampleDate>" + XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc)
+ "</SampleDate></TestOne></TestLevelTwo>";
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
formatter.XmlDictionaryReaderQuotas.MaxStringContentLength = 10;
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
@ -368,7 +509,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void SetMaxDepth_ThrowsWhenMaxDepthIsBelowOne()
{
// Arrange
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
// Act & Assert
Assert.Throws<ArgumentException>(() => formatter.MaxDepth = 0);
@ -380,7 +521,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
// Arrange
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass><SampleInt>10</SampleInt></DummyClass>";
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
@ -412,7 +553,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
Buffer.BlockCopy(inp, 0, contentBytes, inpStart.Length, inp.Length);
Buffer.BlockCopy(inpEnd, 0, contentBytes, inpStart.Length + inp.Length, inpEnd.Length);
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
// Act and Assert
@ -431,7 +572,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
var inputBytes = Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass><SampleInt>1000</SampleInt></DummyClass>");
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var modelState = new ModelStateDictionary();
var httpContext = GetHttpContext(inputBytes, contentType: "application/xml; charset=utf-16");
@ -467,7 +608,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
Buffer.BlockCopy(bom, 0, contentBytes, inputStart.Length, bom.Length);
Buffer.BlockCopy(inputEnd, 0, contentBytes, inputStart.Length + bom.Length, inputEnd.Length);
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
// Act
@ -495,7 +636,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
"<sampleString>" + expectedString + "</sampleString>" +
"<SampleDate>" + expectedDateTime + "</SampleDate></TestLevelOne>";
var formatter = new XmlSerializerInputFormatter();
var formatter = new XmlSerializerInputFormatter(new MvcOptions());
var contentBytes = Encoding.Unicode.GetBytes(input);
var modelState = new ModelStateDictionary();
@ -560,6 +701,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
{
public int createSerializerCalledCount = 0;
public TestXmlSerializerInputFormatter()
: base(new MvcOptions())
{
}
protected override XmlSerializer CreateSerializer(Type type)
{
createSerializerCalledCount++;

View File

@ -30,7 +30,7 @@ namespace XmlFormattersWebSite
// request information (Ex: Accept header).
// So here we instead clear out the default supported media types and create new
// ones which are distinguishable between formatters.
var xmlSerializerInputFormatter = new XmlSerializerInputFormatter();
var xmlSerializerInputFormatter = new XmlSerializerInputFormatter(new MvcOptions());
xmlSerializerInputFormatter.SupportedMediaTypes.Clear();
xmlSerializerInputFormatter.SupportedMediaTypes.Add(
new MediaTypeHeaderValue("application/xml-xmlser"));
@ -44,7 +44,7 @@ namespace XmlFormattersWebSite
xmlSerializerOutputFormatter.SupportedMediaTypes.Add(
new MediaTypeHeaderValue("text/xml-xmlser"));
var dcsInputFormatter = new XmlDataContractSerializerInputFormatter();
var dcsInputFormatter = new XmlDataContractSerializerInputFormatter(new MvcOptions());
dcsInputFormatter.SupportedMediaTypes.Clear();
dcsInputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml-dcs"));
dcsInputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml-dcs"));