[Fixes #852] XmlDictionaryReader.CreateTextReader overload which supports Encoding is missing

This commit is contained in:
Kiran Challa 2015-03-19 10:49:09 -07:00
parent ac908d405e
commit 9a6f8b392d
4 changed files with 74 additions and 16 deletions

View File

@ -6,6 +6,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.AspNet.Mvc.Internal;
@ -95,7 +96,11 @@ namespace Microsoft.AspNet.Mvc.Xml
{
var request = context.ActionContext.HttpContext.Request;
using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body)))
MediaTypeHeaderValue requestContentType;
MediaTypeHeaderValue.TryParse(request.ContentType , out requestContentType);
var effectiveEncoding = SelectCharacterEncoding(requestContentType);
using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body), effectiveEncoding))
{
var type = GetSerializableType(context.ModelType);
@ -131,11 +136,11 @@ namespace Microsoft.AspNet.Mvc.Xml
/// Called during deserialization to get the <see cref="XmlReader"/>.
/// </summary>
/// <param name="readStream">The <see cref="Stream"/> from which to read.</param>
/// <param name="encoding">The <see cref="Encoding"/> used to read the stream.</param>
/// <returns>The <see cref="XmlReader"/> used during deserialization.</returns>
protected virtual XmlReader CreateXmlReader([NotNull] Stream readStream)
protected virtual XmlReader CreateXmlReader([NotNull] Stream readStream, [NotNull] Encoding encoding)
{
return XmlDictionaryReader.CreateTextReader(
readStream, _readerQuotas);
return XmlDictionaryReader.CreateTextReader(readStream, encoding, _readerQuotas, onClose: null);
}
/// <summary>

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
@ -71,7 +72,11 @@ namespace Microsoft.AspNet.Mvc.Xml
{
var request = context.ActionContext.HttpContext.Request;
using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body)))
MediaTypeHeaderValue requestContentType;
MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType);
var effectiveEncoding = SelectCharacterEncoding(requestContentType);
using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body), effectiveEncoding))
{
var type = GetSerializableType(context.ModelType);
@ -116,11 +121,11 @@ namespace Microsoft.AspNet.Mvc.Xml
/// Called during deserialization to get the <see cref="XmlReader"/>.
/// </summary>
/// <param name="readStream">The <see cref="Stream"/> from which to read.</param>
/// <param name="encoding">The <see cref="Encoding"/> used to read the stream.</param>
/// <returns>The <see cref="XmlReader"/> used during deserialization.</returns>
protected virtual XmlReader CreateXmlReader([NotNull] Stream readStream)
protected virtual XmlReader CreateXmlReader([NotNull] Stream readStream, [NotNull] Encoding encoding)
{
return XmlDictionaryReader.CreateTextReader(
readStream, _readerQuotas);
return XmlDictionaryReader.CreateTextReader(readStream, encoding, _readerQuotas, onClose: null);
}
/// <summary>

View File

@ -289,15 +289,15 @@ namespace Microsoft.AspNet.Mvc.Xml
}
[Fact]
public async Task ReadAsync_ThrowsOnInvalidCharacters()
public async Task ReadAsync_FallsbackToUTF8_WhenCharSet_NotInContentType()
{
// Arrange
var expectedException = TestPlatformHelper.IsMono ? typeof(SerializationException) :
typeof(XmlException);
var expectedMessage = TestPlatformHelper.IsMono ?
"Expected element 'TestLevelTwo' in namespace '', but found Element node 'DummyClass' in namespace ''" :
"The encoding in the declaration 'UTF-8' does not match the encoding of the document 'utf-16LE'.";
var inpStart = Encodings.UTF16EncodingLittleEndian.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"The expected encoding 'utf-8' does not match the actual encoding 'utf-16LE'.";
var inpStart = Encodings.UTF16EncodingLittleEndian.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-16\"?>" +
"<DummyClass><SampleInt>");
byte[] inp = { 192, 193 };
var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes("</SampleInt></DummyClass>");
@ -315,6 +315,27 @@ namespace Microsoft.AspNet.Mvc.Xml
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public async Task ReadAsync_UsesContentTypeCharSet_ToReadStream()
{
// Arrange
var expectedException = TestPlatformHelper.IsMono ? typeof(SerializationException) :
typeof(XmlException);
var expectedMessage = TestPlatformHelper.IsMono ?
"Expected element 'TestLevelTwo' in namespace '', but found Element node 'DummyClass' in namespace ''" :
"The expected encoding 'utf-16LE' does not match the actual encoding 'utf-8'.";
var inputBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass><SampleInt>1000</SampleInt></DummyClass>");
var formatter = new XmlDataContractSerializerInputFormatter();
var actionContext = GetActionContext(inputBytes, contentType: "application/xml; charset=utf-16");
var context = new InputFormatterContext(actionContext, typeof(TestLevelOne));
// Act
var ex = await Assert.ThrowsAsync(expectedException, () => formatter.ReadAsync(context));
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public async Task ReadAsync_IgnoresBOMCharacters()
{
@ -359,8 +380,10 @@ namespace Microsoft.AspNet.Mvc.Xml
var formatter = new XmlDataContractSerializerInputFormatter();
var contentBytes = Encodings.UTF16EncodingLittleEndian.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne));
var actionContext = GetActionContext(contentBytes, contentType: "application/xml; charset=utf-16");
var context = new InputFormatterContext(actionContext, typeof(TestLevelOne));
// Act
var model = await formatter.ReadAsync(context);

View File

@ -296,16 +296,16 @@ namespace Microsoft.AspNet.Mvc.Xml
}
[Fact]
public async Task ReadAsync_ThrowsOnInvalidCharacters()
public async Task ReadAsync_FallsbackToUTF8_WhenCharSet_NotInContentType()
{
// Arrange
var expectedException = TestPlatformHelper.IsMono ? typeof(InvalidOperationException) :
typeof(XmlException);
var expectedMessage = TestPlatformHelper.IsMono ?
"There is an error in XML document." :
"The encoding in the declaration 'UTF-8' does not match the encoding of the document 'utf-16LE'.";
"The expected encoding 'utf-8' does not match the actual encoding 'utf-16LE'.";
var inpStart = Encodings.UTF16EncodingLittleEndian.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
var inpStart = Encodings.UTF16EncodingLittleEndian.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-16\"?>" +
"<DummyClass><SampleInt>");
byte[] inp = { 192, 193 };
var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes("</SampleInt></DummyClass>");
@ -323,6 +323,28 @@ namespace Microsoft.AspNet.Mvc.Xml
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public async Task ReadAsync_UsesContentTypeCharSet_ToReadStream()
{
// Arrange
var expectedException = TestPlatformHelper.IsMono ? typeof(InvalidOperationException) :
typeof(XmlException);
var expectedMessage = TestPlatformHelper.IsMono ?
"There is an error in XML document." :
"The expected encoding 'utf-16LE' does not match the actual encoding 'utf-8'.";
var inputBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass><SampleInt>1000</SampleInt></DummyClass>");
var formatter = new XmlSerializerInputFormatter();
var actionContext = GetActionContext(inputBytes, contentType: "application/xml; charset=utf-16");
var context = new InputFormatterContext(actionContext, typeof(TestLevelOne));
// Act and Assert
var ex = await Assert.ThrowsAsync(expectedException, () => formatter.ReadAsync(context));
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public async Task ReadAsync_IgnoresBOMCharacters()
{
@ -369,7 +391,9 @@ namespace Microsoft.AspNet.Mvc.Xml
var formatter = new XmlSerializerInputFormatter();
var contentBytes = Encodings.UTF16EncodingLittleEndian.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne));
var actionContext = GetActionContext(contentBytes, contentType: "application/xml; charset=utf-16");
var context = new InputFormatterContext(actionContext, typeof(TestLevelOne));
// Act
var model = await formatter.ReadAsync(context);
@ -398,6 +422,7 @@ namespace Microsoft.AspNet.Mvc.Xml
new AspNet.Routing.RouteData(),
new ActionDescriptor());
}
private static HttpContext GetHttpContext(byte[] contentBytes,
string contentType = "application/xml")
{