Added CanReadType to InputFormatter
This commit is contained in:
parent
bb31e2aec5
commit
d236d4ffde
|
|
@ -43,6 +43,11 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// <inheritdoc />
|
||||
public virtual bool CanRead(InputFormatterContext context)
|
||||
{
|
||||
if (!CanReadType(context.ModelType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentType = context.ActionContext.HttpContext.Request.ContentType;
|
||||
MediaTypeHeaderValue requestContentType;
|
||||
if (!MediaTypeHeaderValue.TryParse(contentType, out requestContentType))
|
||||
|
|
@ -54,6 +59,16 @@ namespace Microsoft.AspNet.Mvc
|
|||
.Any(supportedMediaType => supportedMediaType.IsSubsetOf(requestContentType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether or not the given type can be read by this serializer.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of object that will be read.</param>
|
||||
/// <returns><c>true</c> if the type can be read, otherwise <c>false</c>.</returns>
|
||||
protected virtual bool CanReadType(Type type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<object> ReadAsync(InputFormatterContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// 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;
|
||||
|
|
@ -19,6 +20,7 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
public class XmlDataContractSerializerInputFormatter : InputFormatter
|
||||
{
|
||||
private DataContractSerializerSettings _serializerSettings;
|
||||
private ConcurrentDictionary<Type, object> _serializerCache = new ConcurrentDictionary<Type, object>();
|
||||
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -93,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
{
|
||||
var type = GetSerializableType(context.ModelType);
|
||||
|
||||
var serializer = CreateSerializer(type);
|
||||
var serializer = GetCachedSerializer(type);
|
||||
|
||||
var deserializedObject = serializer.ReadObject(xmlReader);
|
||||
|
||||
|
|
@ -111,6 +113,12 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool CanReadType([NotNull] Type type)
|
||||
{
|
||||
return GetCachedSerializer(GetSerializableType(type)) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called during deserialization to get the <see cref="XmlReader"/>.
|
||||
/// </summary>
|
||||
|
|
@ -142,7 +150,36 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
/// <returns>The <see cref="DataContractSerializer"/> used during deserialization.</returns>
|
||||
protected virtual DataContractSerializer CreateSerializer([NotNull] Type type)
|
||||
{
|
||||
return new DataContractSerializer(type, _serializerSettings);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cached serializer or creates and caches the serializer for the given type.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="DataContractSerializer"/> instance.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// 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;
|
||||
|
|
@ -19,6 +20,7 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
public class XmlDataContractSerializerOutputFormatter : OutputFormatter
|
||||
{
|
||||
private DataContractSerializerSettings _serializerSettings;
|
||||
private ConcurrentDictionary<Type, object> _serializerCache = new ConcurrentDictionary<Type, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="XmlDataContractSerializerOutputFormatter"/>
|
||||
|
|
@ -115,8 +117,12 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
protected override bool CanWriteType(Type declaredType, Type runtimeType)
|
||||
{
|
||||
var type = ResolveType(declaredType, runtimeType);
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CreateSerializer(GetSerializableType(type)) != null;
|
||||
return GetCachedSerializer(GetSerializableType(type)) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -183,11 +189,30 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
obj = wrapperProvider.Wrap(obj);
|
||||
}
|
||||
|
||||
var dataContractSerializer = CreateSerializer(wrappingType);
|
||||
var dataContractSerializer = GetCachedSerializer(wrappingType);
|
||||
dataContractSerializer.WriteObject(xmlWriter, obj);
|
||||
}
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cached serializer or creates and caches the serializer for the given type.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="DataContractSerializer"/> instance.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
|
@ -18,6 +19,7 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
/// </summary>
|
||||
public class XmlSerializerInputFormatter : InputFormatter
|
||||
{
|
||||
private ConcurrentDictionary<Type, object> _serializerCache = new ConcurrentDictionary<Type, object>();
|
||||
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -72,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
{
|
||||
var type = GetSerializableType(context.ModelType);
|
||||
|
||||
var serializer = CreateSerializer(type);
|
||||
var serializer = GetCachedSerializer(type);
|
||||
|
||||
var deserializedObject = serializer.Deserialize(xmlReader);
|
||||
|
||||
|
|
@ -90,6 +92,12 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool CanReadType([NotNull] Type type)
|
||||
{
|
||||
return GetCachedSerializer(GetSerializableType(type)) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type to which the XML will be deserialized.
|
||||
/// </summary>
|
||||
|
|
@ -120,7 +128,36 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
/// <returns>The <see cref="XmlSerializer"/> used during deserialization.</returns>
|
||||
protected virtual XmlSerializer CreateSerializer(Type type)
|
||||
{
|
||||
return new XmlSerializer(type);
|
||||
try
|
||||
{
|
||||
// If the serializer does not support this type it will throw an exception.
|
||||
return new XmlSerializer(type);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cached serializer or creates and caches the serializer for the given type.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="XmlSerializer"/> instance.</returns>
|
||||
protected virtual XmlSerializer GetCachedSerializer(Type type)
|
||||
{
|
||||
object serializer;
|
||||
if (!_serializerCache.TryGetValue(type, out serializer))
|
||||
{
|
||||
serializer = CreateSerializer(type);
|
||||
if (serializer != null)
|
||||
{
|
||||
_serializerCache.TryAdd(type, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
return (XmlSerializer) serializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
|
@ -18,6 +19,8 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
/// </summary>
|
||||
public class XmlSerializerOutputFormatter : OutputFormatter
|
||||
{
|
||||
private ConcurrentDictionary<Type, object> _serializerCache = new ConcurrentDictionary<Type, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="XmlSerializerOutputFormatter"/>
|
||||
/// with default XmlWriterSettings.
|
||||
|
|
@ -93,8 +96,12 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
protected override bool CanWriteType(Type declaredType, Type runtimeType)
|
||||
{
|
||||
var type = ResolveType(declaredType, runtimeType);
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CreateSerializer(GetSerializableType(type)) != null;
|
||||
return GetCachedSerializer(GetSerializableType(type)) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -159,11 +166,30 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
obj = wrapperProvider.Wrap(obj);
|
||||
}
|
||||
|
||||
var xmlSerializer = CreateSerializer(wrappingType);
|
||||
var xmlSerializer = GetCachedSerializer(wrappingType);
|
||||
xmlSerializer.Serialize(xmlWriter, obj);
|
||||
}
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cached serializer or creates and caches the serializer for the given type.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="XmlSerializer"/> instance.</returns>
|
||||
protected virtual XmlSerializer GetCachedSerializer(Type type)
|
||||
{
|
||||
object serializer;
|
||||
if (!_serializerCache.TryGetValue(type, out serializer))
|
||||
{
|
||||
serializer = CreateSerializer(type);
|
||||
if (serializer != null)
|
||||
{
|
||||
_serializerCache.TryAdd(type, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
return (XmlSerializer)serializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ using System.Threading.Tasks;
|
|||
using System.Xml;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Xml;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -79,6 +78,24 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
Assert.Equal(expectedCanRead, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void XmlDataContractSerializer_CachesSerializerForType()
|
||||
{
|
||||
// Arrange
|
||||
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
|
||||
"<DummyClass><SampleInt>10</SampleInt></DummyClass>";
|
||||
var formatter = new TestXmlDataContractSerializerInputFormatter();
|
||||
var contentBytes = Encoding.UTF8.GetBytes(input);
|
||||
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
|
||||
|
||||
// Act
|
||||
formatter.CanRead(context);
|
||||
formatter.CanRead(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, formatter.createSerializerCalledCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasProperSuppportedMediaTypes()
|
||||
{
|
||||
|
|
@ -485,5 +502,16 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
httpContext.SetupGet(c => c.Request).Returns(request.Object);
|
||||
return httpContext.Object;
|
||||
}
|
||||
|
||||
private class TestXmlDataContractSerializerInputFormatter : XmlDataContractSerializerInputFormatter
|
||||
{
|
||||
public int createSerializerCalledCount = 0;
|
||||
|
||||
protected override DataContractSerializer CreateSerializer(Type type)
|
||||
{
|
||||
createSerializerCalledCount++;
|
||||
return base.CreateSerializer(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -111,6 +111,22 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void XmlDataContractSerializer_CachesSerializerForType()
|
||||
{
|
||||
// Arrange
|
||||
var input = new DummyClass { SampleInt = 10 };
|
||||
var formatter = new TestXmlDataContractSerializerOutputFormatter();
|
||||
var context = GetOutputFormatterContext(input, typeof(DummyClass));
|
||||
|
||||
// Act
|
||||
formatter.CanWriteResult(context, MediaTypeHeaderValue.Parse("application/xml"));
|
||||
formatter.CanWriteResult(context, MediaTypeHeaderValue.Parse("application/xml"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, formatter.createSerializerCalledCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultConstructor_ExpectedWriterSettings_Created()
|
||||
{
|
||||
|
|
@ -552,5 +568,16 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
httpContext.SetupGet(c => c.Response).Returns(response.Object);
|
||||
return new ActionContext(httpContext.Object, routeData: null, actionDescriptor: null);
|
||||
}
|
||||
|
||||
private class TestXmlDataContractSerializerOutputFormatter : XmlDataContractSerializerOutputFormatter
|
||||
{
|
||||
public int createSerializerCalledCount = 0;
|
||||
|
||||
protected override DataContractSerializer CreateSerializer(Type type)
|
||||
{
|
||||
createSerializerCalledCount++;
|
||||
return base.CreateSerializer(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
#if ASPNET50
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Xml;
|
||||
|
|
@ -64,6 +66,42 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
Assert.Equal(expectedCanRead, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(Dictionary<string, object>), false)]
|
||||
[InlineData(typeof(string), true)]
|
||||
public void CanRead_ReturnsFalse_ForAnyUnsupportedModelType(Type modelType, bool expectedCanRead)
|
||||
{
|
||||
// Arrange
|
||||
var formatter = new XmlSerializerInputFormatter();
|
||||
var contentBytes = Encoding.UTF8.GetBytes("content");
|
||||
|
||||
var context = GetInputFormatterContext(contentBytes, modelType);
|
||||
|
||||
// Act
|
||||
var result = formatter.CanRead(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedCanRead, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void XmlSerializer_CachesSerializerForType()
|
||||
{
|
||||
// Arrange
|
||||
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
|
||||
"<DummyClass><SampleInt>10</SampleInt></DummyClass>";
|
||||
var formatter = new TestXmlSerializerInputFormatter();
|
||||
var contentBytes = Encoding.UTF8.GetBytes(input);
|
||||
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
|
||||
|
||||
// Act
|
||||
formatter.CanRead(context);
|
||||
formatter.CanRead(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, formatter.createSerializerCalledCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasProperSuppportedMediaTypes()
|
||||
{
|
||||
|
|
@ -374,6 +412,17 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
httpContext.SetupGet(c => c.Request).Returns(request.Object);
|
||||
return httpContext.Object;
|
||||
}
|
||||
|
||||
private class TestXmlSerializerInputFormatter : XmlSerializerInputFormatter
|
||||
{
|
||||
public int createSerializerCalledCount = 0;
|
||||
|
||||
protected override XmlSerializer CreateSerializer(Type type)
|
||||
{
|
||||
createSerializerCalledCount++;
|
||||
return base.CreateSerializer(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Microsoft.Net.Http.Headers;
|
|||
using Microsoft.AspNet.Mvc.Xml;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Xml
|
||||
{
|
||||
|
|
@ -68,6 +69,22 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void XmlSerializer_CachesSerializerForType()
|
||||
{
|
||||
// Arrange
|
||||
var input = new DummyClass { SampleInt = 10 };
|
||||
var formatter = new TestXmlSerializerOutputFormatter();
|
||||
var context = GetOutputFormatterContext(input, typeof(DummyClass));
|
||||
|
||||
// Act
|
||||
formatter.CanWriteResult(context, MediaTypeHeaderValue.Parse("application/xml"));
|
||||
formatter.CanWriteResult(context, MediaTypeHeaderValue.Parse("application/xml"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, formatter.createSerializerCalledCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultConstructor_ExpectedWriterSettings_Created()
|
||||
{
|
||||
|
|
@ -355,5 +372,16 @@ namespace Microsoft.AspNet.Mvc.Xml
|
|||
httpContext.SetupGet(c => c.Response).Returns(response.Object);
|
||||
return new ActionContext(httpContext.Object, routeData: null, actionDescriptor: null);
|
||||
}
|
||||
|
||||
private class TestXmlSerializerOutputFormatter : XmlSerializerOutputFormatter
|
||||
{
|
||||
public int createSerializerCalledCount = 0;
|
||||
|
||||
protected override XmlSerializer CreateSerializer(Type type)
|
||||
{
|
||||
createSerializerCalledCount++;
|
||||
return base.CreateSerializer(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue