diff --git a/Mvc.sln b/Mvc.sln
index dad54ecc62..636e42cb3c 100644
--- a/Mvc.sln
+++ b/Mvc.sln
@@ -54,6 +54,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CompositeViewEngine", "test
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RazorWebSite", "test\WebSites\RazorWebSite\RazorWebSite.kproj", "{B07CAF59-11ED-40E3-A5DB-E1178F84FA78}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "FormatterWebSite", "test\WebSites\FormatterWebSite\FormatterWebSite.kproj", "{62735776-46FF-4170-9392-02E128A69B89}"
+EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ValueProvidersSite", "test\WebSites\ValueProvidersSite\ValueProvidersSite.kproj", "{14F79E79-AE79-48FA-95DE-D794EF4EABB3}"
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.HeaderValueAbstractions", "src\Microsoft.AspNet.Mvc.HeaderValueAbstractions\Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj", "{98335B23-E4B9-4CAD-9749-0DED32A659A1}"
EndProject
@@ -279,6 +281,16 @@ Global
{B07CAF59-11ED-40E3-A5DB-E1178F84FA78}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B07CAF59-11ED-40E3-A5DB-E1178F84FA78}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B07CAF59-11ED-40E3-A5DB-E1178F84FA78}.Release|x86.ActiveCfg = Release|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Release|Any CPU.Build.0 = Release|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {62735776-46FF-4170-9392-02E128A69B89}.Release|x86.ActiveCfg = Release|Any CPU
{14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -336,6 +348,7 @@ Global
{5F945B82-FE5F-425C-956C-8BC2F2020254} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{A853B2BA-4449-4908-A416-5A3C027FC22B} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{B07CAF59-11ED-40E3-A5DB-E1178F84FA78} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
+ {62735776-46FF-4170-9392-02E128A69B89} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{14F79E79-AE79-48FA-95DE-D794EF4EABB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{98335B23-E4B9-4CAD-9749-0DED32A659A1} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{E69FD235-2042-43A4-9970-59CB29955B4E} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
diff --git a/src/Microsoft.AspNet.Mvc.Common/Encodings.cs b/src/Microsoft.AspNet.Mvc.Common/Encodings.cs
index 714a78cd4f..5ab65d219a 100644
--- a/src/Microsoft.AspNet.Mvc.Common/Encodings.cs
+++ b/src/Microsoft.AspNet.Mvc.Common/Encodings.cs
@@ -16,8 +16,7 @@ namespace Microsoft.AspNet.Mvc
///
/// Returns UTF16 Encoding which uses littleEndian byte order with BOM and throws on invalid bytes.
///
- public static readonly Encoding UnicodeEncodingWithBOM = new UnicodeEncoding(bigEndian: false,
- byteOrderMark: true,
- throwOnInvalidBytes: true);
+ public static readonly Encoding UTF16EncodingLittleEndian
+ = new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs
index 238ff7e86c..80f443b5f5 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs
@@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc
_settings = settings;
_indent = indent;
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
- SupportedEncodings.Add(Encodings.UnicodeEncodingWithBOM);
+ SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/json"));
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/project.json b/src/Microsoft.AspNet.Mvc.Core/project.json
index 45e48450fe..9bd0450c0e 100644
--- a/src/Microsoft.AspNet.Mvc.Core/project.json
+++ b/src/Microsoft.AspNet.Mvc.Core/project.json
@@ -18,7 +18,11 @@
"Newtonsoft.Json": "5.0.8"
},
"frameworks": {
- "net45": {},
+ "net45": {
+ "dependencies": {
+ "System.Xml": ""
+ }
+ },
"k10": {
"dependencies": {
"Microsoft.CSharp": "4.0.0.0",
@@ -41,13 +45,16 @@
"System.Runtime": "4.0.20.0",
"System.Runtime.Extensions": "4.0.10.0",
"System.Runtime.InteropServices": "4.0.20.0",
+ "System.Runtime.Serialization.Xml": "4.0.0.0",
"System.Security.Claims": "1.0.0-*",
"System.Security.Cryptography.Encryption": "4.0.0.0",
"System.Security.Cryptography.Hashing.Algorithms": "4.0.0.0",
"System.Security.Principal": "4.0.0.0",
"System.Text.Encoding": "4.0.20.0",
"System.Threading": "4.0.0.0",
- "System.Threading.Tasks": "4.0.10.0"
+ "System.Threading.Tasks": "4.0.10.0",
+ "System.Xml.ReaderWriter": "4.0.0.0",
+ "System.Xml.XmlSerializer": "4.0.0.0"
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/DelegatingStream.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/DelegatingStream.cs
new file mode 100644
index 0000000000..d807799f27
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/DelegatingStream.cs
@@ -0,0 +1,189 @@
+// Copyright (c) Microsoft Open Technologies, Inc. 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.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding
+{
+ ///
+ /// Stream that delegates to an inner stream.
+ /// This Stream is present so that the inner stream is not closed
+ /// even when Close() or Dispose() is called.
+ ///
+ public class DelegatingStream : Stream
+ {
+ private readonly Stream _innerStream;
+
+ ///
+ /// Initializes a new object of DelegatingStream
+ ///
+ /// The stream on which should not be closed
+ public DelegatingStream([NotNull] Stream innerStream)
+ {
+ _innerStream = innerStream;
+ }
+
+ protected Stream InnerStream
+ {
+ get { return _innerStream; }
+ }
+
+ ///
+ public override bool CanRead
+ {
+ get { return _innerStream.CanRead; }
+ }
+
+ ///
+ public override bool CanSeek
+ {
+ get { return _innerStream.CanSeek; }
+ }
+
+ ///
+ public override bool CanWrite
+ {
+ get { return _innerStream.CanWrite; }
+ }
+
+ ///
+ public override long Length
+ {
+ get { return _innerStream.Length; }
+ }
+
+ ///
+ public override long Position
+ {
+ get { return _innerStream.Position; }
+ set { _innerStream.Position = value; }
+ }
+
+ ///
+ public override int ReadTimeout
+ {
+ get { return _innerStream.ReadTimeout; }
+ set { _innerStream.ReadTimeout = value; }
+ }
+
+ ///
+ public override bool CanTimeout
+ {
+ get { return _innerStream.CanTimeout; }
+ }
+
+ ///
+ public override int WriteTimeout
+ {
+ get { return _innerStream.WriteTimeout; }
+ set { _innerStream.WriteTimeout = value; }
+ }
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return _innerStream.Seek(offset, origin);
+ }
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return _innerStream.Read(buffer, offset, count);
+ }
+
+ ///
+ public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ return _innerStream.ReadAsync(buffer, offset, count, cancellationToken);
+ }
+#if NET45
+ ///
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count,
+ AsyncCallback callback, object state)
+ {
+ return _innerStream.BeginRead(buffer, offset, count, callback, state);
+ }
+
+ ///
+ public override int EndRead(IAsyncResult asyncResult)
+ {
+ return _innerStream.EndRead(asyncResult);
+ }
+#endif
+ ///
+ public override int ReadByte()
+ {
+ return _innerStream.ReadByte();
+ }
+
+ ///
+ public override void Flush()
+ {
+ _innerStream.Flush();
+ }
+
+ ///
+ public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+ {
+ return _innerStream.CopyToAsync(destination, bufferSize, cancellationToken);
+ }
+
+ ///
+ public override Task FlushAsync(CancellationToken cancellationToken)
+ {
+ return _innerStream.FlushAsync(cancellationToken);
+ }
+
+ ///
+ public override void SetLength(long value)
+ {
+ _innerStream.SetLength(value);
+ }
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ _innerStream.Write(buffer, offset, count);
+ }
+
+ ///
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ return _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
+ }
+#if NET45
+ ///
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count,
+ AsyncCallback callback, object state)
+ {
+ return _innerStream.BeginWrite(buffer, offset, count, callback, state);
+ }
+
+ ///
+ public override void EndWrite(IAsyncResult asyncResult)
+ {
+ _innerStream.EndWrite(asyncResult);
+ }
+#endif
+ ///
+ public override void WriteByte(byte value)
+ {
+ _innerStream.WriteByte(value);
+ }
+#if NET45
+ ///
+ public override void Close()
+ {
+ }
+#endif
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ // No-op. In CoreCLR this is equivalent to Close.
+ // Given that we don't own the underlying stream, we never want to do anything interesting here.
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/FormattingUtilities.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/FormattingUtilities.cs
new file mode 100644
index 0000000000..9edcb396e6
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/FormattingUtilities.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Open Technologies, Inc. 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.Xml;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding
+{
+ ///
+ /// Contains methods which are used by input formatters.
+ ///
+ public static class FormattingUtilities
+ {
+ public static readonly int DefaultMaxDepth = 32;
+
+ ///
+ /// Gets the default Reader Quotas for XmlReader.
+ ///
+ /// XmlReaderQuotas with default values
+ public static XmlDictionaryReaderQuotas GetDefaultXmlReaderQuotas()
+ {
+#if NET45
+ return new XmlDictionaryReaderQuotas()
+ {
+ MaxArrayLength = Int32.MaxValue,
+ MaxBytesPerRead = Int32.MaxValue,
+ MaxDepth = DefaultMaxDepth,
+ MaxNameTableCharCount = Int32.MaxValue,
+ MaxStringContentLength = Int32.MaxValue
+ };
+#else
+ return XmlDictionaryReaderQuotas.Max;
+#endif
+ }
+
+ /// Internal because ContentTypeHeaderValue is internal.
+ internal static Encoding SelectCharacterEncoding(IList supportedEncodings,
+ ContentTypeHeaderValue contentType, Type callerType)
+ {
+ if (contentType != null)
+ {
+ // Find encoding based on content type charset parameter
+ var charset = contentType.CharSet;
+ if (!string.IsNullOrWhiteSpace(contentType.CharSet))
+ {
+ for (var i = 0; i < supportedEncodings.Count; i++)
+ {
+ var supportedEncoding = supportedEncodings[i];
+ if (string.Equals(charset, 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.
+ throw new InvalidOperationException(Resources.FormatMediaTypeFormatterNoEncoding(callerType.FullName));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlDataContractSerializerInputFormatter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlDataContractSerializerInputFormatter.cs
new file mode 100644
index 0000000000..a8a06c197f
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlDataContractSerializerInputFormatter.cs
@@ -0,0 +1,136 @@
+// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding
+{
+ ///
+ /// This class handles deserialization of input XML data
+ /// to strongly-typed objects using .
+ ///
+ public class XmlDataContractSerializerInputFormatter : IInputFormatter
+ {
+ private readonly IList _supportedEncodings;
+ private readonly IList _supportedMediaTypes;
+ private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
+
+ ///
+ /// Initializes a new instance of DataContractSerializerInputFormatter
+ ///
+ public XmlDataContractSerializerInputFormatter()
+ {
+ _supportedMediaTypes = new List
+ {
+ "application/xml",
+ "text/xml"
+ };
+
+ _supportedEncodings = new List
+ {
+ Encodings.UTF8EncodingWithoutBOM,
+ Encodings.UTF16EncodingLittleEndian
+ };
+ }
+
+ ///
+ /// Returns the list of supported encodings.
+ ///
+ public IList SupportedEncodings
+ {
+ get { return _supportedEncodings; }
+ }
+
+ ///
+ /// Returns the list of supported Media Types.
+ ///
+ public IList SupportedMediaTypes
+ {
+ get { return _supportedMediaTypes; }
+ }
+
+ ///
+ /// Indicates the acceptable input XML depth.
+ ///
+ public int MaxDepth
+ {
+ get { return _readerQuotas.MaxDepth; }
+ set { _readerQuotas.MaxDepth = value; }
+ }
+
+ ///
+ /// The quotas include - DefaultMaxDepth, DefaultMaxStringContentLength, DefaultMaxArrayLength,
+ /// DefaultMaxBytesPerRead, DefaultMaxNameTableCharCount
+ ///
+ public XmlDictionaryReaderQuotas XmlDictionaryReaderQuotas
+ {
+ get { return _readerQuotas; }
+ }
+
+ ///
+ /// Reads the input XML.
+ ///
+ /// The input formatter context which contains the body to be read.
+ /// Task which reads the input.
+ public async Task ReadAsync(InputFormatterContext context)
+ {
+ var request = context.HttpContext.Request;
+ if (request.ContentLength == 0)
+ {
+ context.Model = GetDefaultValueForType(context.Metadata.ModelType);
+ return;
+ }
+
+ context.Model = await ReadInternal(context);
+ }
+
+ ///
+ /// Called during deserialization to get the .
+ ///
+ /// The from which to read.
+ /// The used during deserialization.
+ protected virtual XmlReader CreateXmlReader([NotNull] Stream readStream)
+ {
+ return XmlDictionaryReader.CreateTextReader(
+ readStream, _readerQuotas);
+ }
+
+ ///
+ /// Called during deserialization to get the .
+ ///
+ /// The used during deserialization.
+ protected virtual XmlObjectSerializer CreateDataContractSerializer(Type type)
+ {
+ return new DataContractSerializer(type);
+ }
+
+ private object GetDefaultValueForType(Type modelType)
+ {
+ if (modelType.GetTypeInfo().IsValueType)
+ {
+ return Activator.CreateInstance(modelType);
+ }
+
+ return null;
+ }
+
+ private Task