diff --git a/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
index 70fff6deaa..73ec63d9c4 100644
--- a/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
@@ -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 .
///
/// The from which to read.
+ /// The used to read the stream.
/// The used during deserialization.
- 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);
}
///
diff --git a/src/Microsoft.AspNet.Mvc.Xml/XmlSerializerInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Xml/XmlSerializerInputFormatter.cs
index 78a149e539..4aaaebe095 100644
--- a/src/Microsoft.AspNet.Mvc.Xml/XmlSerializerInputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Xml/XmlSerializerInputFormatter.cs
@@ -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 .
///
/// The from which to read.
+ /// The used to read the stream.
/// The used during deserialization.
- 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);
}
///
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
index 680e2c2a68..bfde8721ef 100644
--- a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
@@ -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("" +
+ "The expected encoding 'utf-8' does not match the actual encoding 'utf-16LE'.";
+ var inpStart = Encodings.UTF16EncodingLittleEndian.GetBytes("" +
"");
byte[] inp = { 192, 193 };
var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes("");
@@ -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("" +
+ "1000");
+
+ 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);
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs
index cb103062bd..212e6c48e3 100644
--- a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs
@@ -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("" +
+ var inpStart = Encodings.UTF16EncodingLittleEndian.GetBytes("" +
"");
byte[] inp = { 192, 193 };
var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes("");
@@ -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("" +
+ "1000");
+
+ 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")
{