// Copyright (c) .NET Foundation. 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.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; using Microsoft.Framework.Internal; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNet.Mvc.Xml { /// /// This class handles deserialization of input XML data /// to strongly-typed objects using . /// public class XmlDataContractSerializerInputFormatter : InputFormatter { private DataContractSerializerSettings _serializerSettings; private ConcurrentDictionary _serializerCache = new ConcurrentDictionary(); private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas(); /// /// Initializes a new instance of DataContractSerializerInputFormatter /// public XmlDataContractSerializerInputFormatter() { SupportedEncodings.Add(UTF8EncodingWithoutBOM); SupportedEncodings.Add(UTF16EncodingLittleEndian); SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationXml); SupportedMediaTypes.Add(MediaTypeHeaderValues.TextXml); _serializerSettings = new DataContractSerializerSettings(); WrapperProviderFactories = new List(); WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory()); } /// /// Gets the list of to /// provide the wrapping type for de-serialization. /// public IList WrapperProviderFactories { get; } /// /// 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; } } /// /// Gets or sets the used to configure the /// . /// public DataContractSerializerSettings SerializerSettings { get { return _serializerSettings; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _serializerSettings = value; } } /// /// Reads the input XML. /// /// The input formatter context which contains the body to be read. /// Task which reads the input. public override Task ReadRequestBodyAsync(InputFormatterContext context) { var request = context.HttpContext.Request; 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 serializer = GetCachedSerializer(type); var deserializedObject = serializer.ReadObject(xmlReader); // Unwrap only if the original type was wrapped. if (type != context.ModelType) { var unwrappable = deserializedObject as IUnwrappable; if (unwrappable != null) { deserializedObject = unwrappable.Unwrap(declaredType: context.ModelType); } } return Task.FromResult(deserializedObject); } } /// protected override bool CanReadType([NotNull] Type type) { return GetCachedSerializer(GetSerializableType(type)) != null; } /// /// 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, [NotNull] Encoding encoding) { return XmlDictionaryReader.CreateTextReader(readStream, encoding, _readerQuotas, onClose: null); } /// /// Gets the type to which the XML will be deserialized. /// /// The declared type. /// The type to which the XML will be deserialized. protected virtual Type GetSerializableType([NotNull] Type declaredType) { var wrapperProvider = WrapperProviderFactories.GetWrapperProvider( new WrapperProviderContext(declaredType, isSerialization: false)); return wrapperProvider?.WrappingType ?? declaredType; } /// /// Called during deserialization to get the . /// /// The type of object for which the serializer should be created. /// The used during deserialization. protected virtual DataContractSerializer CreateSerializer([NotNull] Type type) { try { // If the serializer does not support this type it will throw an exception. return new DataContractSerializer(type, _serializerSettings); } catch (Exception) { // We do not surface the caught exception because if CanRead returns // false, then this Formatter is not picked up at all. return null; } } /// /// Gets the cached serializer or creates and caches the serializer for the given type. /// /// The instance. protected virtual DataContractSerializer GetCachedSerializer(Type type) { object serializer; if (!_serializerCache.TryGetValue(type, out serializer)) { serializer = CreateSerializer(type); if (serializer != null) { _serializerCache.TryAdd(type, serializer); } } return (DataContractSerializer)serializer; } } }