[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.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.Internal;
@ -95,7 +96,11 @@ namespace Microsoft.AspNet.Mvc.Xml
{ {
var request = context.ActionContext.HttpContext.Request; 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); var type = GetSerializableType(context.ModelType);
@ -131,11 +136,11 @@ namespace Microsoft.AspNet.Mvc.Xml
/// Called during deserialization to get the <see cref="XmlReader"/>. /// Called during deserialization to get the <see cref="XmlReader"/>.
/// </summary> /// </summary>
/// <param name="readStream">The <see cref="Stream"/> from which to read.</param> /// <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> /// <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( return XmlDictionaryReader.CreateTextReader(readStream, encoding, _readerQuotas, onClose: null);
readStream, _readerQuotas);
} }
/// <summary> /// <summary>

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
@ -71,7 +72,11 @@ namespace Microsoft.AspNet.Mvc.Xml
{ {
var request = context.ActionContext.HttpContext.Request; 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); var type = GetSerializableType(context.ModelType);
@ -116,11 +121,11 @@ namespace Microsoft.AspNet.Mvc.Xml
/// Called during deserialization to get the <see cref="XmlReader"/>. /// Called during deserialization to get the <see cref="XmlReader"/>.
/// </summary> /// </summary>
/// <param name="readStream">The <see cref="Stream"/> from which to read.</param> /// <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> /// <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( return XmlDictionaryReader.CreateTextReader(readStream, encoding, _readerQuotas, onClose: null);
readStream, _readerQuotas);
} }
/// <summary> /// <summary>

View File

@ -289,15 +289,15 @@ namespace Microsoft.AspNet.Mvc.Xml
} }
[Fact] [Fact]
public async Task ReadAsync_ThrowsOnInvalidCharacters() public async Task ReadAsync_FallsbackToUTF8_WhenCharSet_NotInContentType()
{ {
// Arrange // Arrange
var expectedException = TestPlatformHelper.IsMono ? typeof(SerializationException) : var expectedException = TestPlatformHelper.IsMono ? typeof(SerializationException) :
typeof(XmlException); typeof(XmlException);
var expectedMessage = TestPlatformHelper.IsMono ? var expectedMessage = TestPlatformHelper.IsMono ?
"Expected element 'TestLevelTwo' in namespace '', but found Element node 'DummyClass' in namespace ''" : "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'."; "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>"); "<DummyClass><SampleInt>");
byte[] inp = { 192, 193 }; byte[] inp = { 192, 193 };
var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes("</SampleInt></DummyClass>"); var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes("</SampleInt></DummyClass>");
@ -315,6 +315,27 @@ namespace Microsoft.AspNet.Mvc.Xml
Assert.Equal(expectedMessage, ex.Message); 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] [Fact]
public async Task ReadAsync_IgnoresBOMCharacters() public async Task ReadAsync_IgnoresBOMCharacters()
{ {
@ -359,7 +380,9 @@ namespace Microsoft.AspNet.Mvc.Xml
var formatter = new XmlDataContractSerializerInputFormatter(); var formatter = new XmlDataContractSerializerInputFormatter();
var contentBytes = Encodings.UTF16EncodingLittleEndian.GetBytes(input); 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 // Act
var model = await formatter.ReadAsync(context); var model = await formatter.ReadAsync(context);

View File

@ -296,16 +296,16 @@ namespace Microsoft.AspNet.Mvc.Xml
} }
[Fact] [Fact]
public async Task ReadAsync_ThrowsOnInvalidCharacters() public async Task ReadAsync_FallsbackToUTF8_WhenCharSet_NotInContentType()
{ {
// Arrange // Arrange
var expectedException = TestPlatformHelper.IsMono ? typeof(InvalidOperationException) : var expectedException = TestPlatformHelper.IsMono ? typeof(InvalidOperationException) :
typeof(XmlException); typeof(XmlException);
var expectedMessage = TestPlatformHelper.IsMono ? var expectedMessage = TestPlatformHelper.IsMono ?
"There is an error in XML document." : "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>"); "<DummyClass><SampleInt>");
byte[] inp = { 192, 193 }; byte[] inp = { 192, 193 };
var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes("</SampleInt></DummyClass>"); var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes("</SampleInt></DummyClass>");
@ -323,6 +323,28 @@ namespace Microsoft.AspNet.Mvc.Xml
Assert.Equal(expectedMessage, ex.Message); 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] [Fact]
public async Task ReadAsync_IgnoresBOMCharacters() public async Task ReadAsync_IgnoresBOMCharacters()
{ {
@ -369,7 +391,9 @@ namespace Microsoft.AspNet.Mvc.Xml
var formatter = new XmlSerializerInputFormatter(); var formatter = new XmlSerializerInputFormatter();
var contentBytes = Encodings.UTF16EncodingLittleEndian.GetBytes(input); 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 // Act
var model = await formatter.ReadAsync(context); var model = await formatter.ReadAsync(context);
@ -398,6 +422,7 @@ namespace Microsoft.AspNet.Mvc.Xml
new AspNet.Routing.RouteData(), new AspNet.Routing.RouteData(),
new ActionDescriptor()); new ActionDescriptor());
} }
private static HttpContext GetHttpContext(byte[] contentBytes, private static HttpContext GetHttpContext(byte[] contentBytes,
string contentType = "application/xml") string contentType = "application/xml")
{ {