Add a System.Text.Json based TempDataSerializer (#8874)
* Add a System.Text.Json based TempDataSerializer * Update DefaultTempDataSerializer * Add common tests for DefaultTempDataSerializer & BsonTempDataSerializer * Remove uses of NewtonsoftJson in tests solely required for temp-data support Fixes https://github.com/aspnet/AspNetCore/issues/7255
This commit is contained in:
parent
8499a27c7f
commit
1b868323e8
|
|
@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new byte[0];
|
return Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,6 +165,8 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool CanSerializeType(Type type) => CanSerializeType(type, out _);
|
||||||
|
|
||||||
private static bool CanSerializeType(Type typeToSerialize, out string errorMessage)
|
private static bool CanSerializeType(Type typeToSerialize, out string errorMessage)
|
||||||
{
|
{
|
||||||
typeToSerialize = typeToSerialize ?? throw new ArgumentNullException(nameof(typeToSerialize));
|
typeToSerialize = typeToSerialize ?? throw new ArgumentNullException(nameof(typeToSerialize));
|
||||||
|
|
@ -208,6 +210,8 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
}
|
}
|
||||||
|
|
||||||
actualType = actualType ?? typeToSerialize;
|
actualType = actualType ?? typeToSerialize;
|
||||||
|
actualType = Nullable.GetUnderlyingType(actualType) ?? actualType;
|
||||||
|
|
||||||
if (!IsSimpleType(actualType))
|
if (!IsSimpleType(actualType))
|
||||||
{
|
{
|
||||||
errorMessage = Resources.FormatTempData_CannotSerializeType(
|
errorMessage = Resources.FormatTempData_CannotSerializeType(
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,15 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
{
|
{
|
||||||
public class BsonTempDataSerializerTest
|
public class BsonTempDataSerializerTest : TempDataSerializerTestBase
|
||||||
{
|
{
|
||||||
|
protected override TempDataSerializer GetTempDataSerializer() => new BsonTempDataSerializer();
|
||||||
|
|
||||||
public static TheoryData<object, Type> InvalidTypes
|
public static TheoryData<object, Type> InvalidTypes
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -93,6 +96,112 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_ArrayOfIntegers()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var value = new[] { 1, -2, 3 };
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<int[]>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_DateTime()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var value = new DateTime(2007, 1, 1);
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<DateTime>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_Guid()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var value = Guid.NewGuid();
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<Guid>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(2147483648)]
|
||||||
|
[InlineData(-2147483649)]
|
||||||
|
public void RoundTripTest_LongValue(long value)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<long>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_Double()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var value = 10d;
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = (double)values[key];
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(ValidTypes))]
|
[MemberData(nameof(ValidTypes))]
|
||||||
public void EnsureObjectCanBeSerialized_DoesNotThrow_OnValidType(object value)
|
public void EnsureObjectCanBeSerialized_DoesNotThrow_OnValidType(object value)
|
||||||
|
|
@ -104,40 +213,6 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
||||||
testProvider.EnsureObjectCanBeSerialized(value);
|
testProvider.EnsureObjectCanBeSerialized(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DeserializeTempData_ReturnsEmptyDictionary_DataIsEmpty()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var serializer = new BsonTempDataSerializer();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var tempDataDictionary = serializer.Deserialize(new byte[0]);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(tempDataDictionary);
|
|
||||||
Assert.Empty(tempDataDictionary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SerializeAndDeserialize_NullValue_RoundTripsSuccessfully()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var key = "NullKey";
|
|
||||||
var testProvider = new BsonTempDataSerializer();
|
|
||||||
var input = new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ key, null }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var bytes = testProvider.Serialize(input);
|
|
||||||
var values = testProvider.Deserialize(bytes);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(values.ContainsKey(key));
|
|
||||||
Assert.Null(values[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestItem
|
private class TestItem
|
||||||
{
|
{
|
||||||
public int DummyInt { get; set; }
|
public int DummyInt { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\..\Mvc.Core\test\Formatters\JsonInputFormatterTestBase.cs" />
|
<Compile Include="..\..\Mvc.Core\test\Formatters\JsonInputFormatterTestBase.cs" />
|
||||||
<Compile Include="..\..\Mvc.Core\test\Formatters\JsonOutputFormatterTestBase.cs" />
|
<Compile Include="..\..\Mvc.Core\test\Formatters\JsonOutputFormatterTestBase.cs" />
|
||||||
|
<Compile Include="..\..\Mvc.ViewFeatures\test\Infrastructure\TempDataSerializerTestBase.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure;
|
||||||
using Moq;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
|
|
@ -44,6 +44,23 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
Assert.Equal(expected, ex.Message);
|
Assert.Equal(expected, ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OnProvidersExecuting_ThrowsIfThePropertyTypeIsUnsupported()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(TestPageModel_InvalidProperties);
|
||||||
|
var expected = $"TempData serializer '{typeof(DefaultTempDataSerializer)}' cannot serialize property '{type}.ModelState' of type '{typeof(ModelStateDictionary)}'." +
|
||||||
|
Environment.NewLine +
|
||||||
|
$"TempData serializer '{typeof(DefaultTempDataSerializer)}' cannot serialize property '{type}.TimeZone' of type '{typeof(TimeZoneInfo)}'.";
|
||||||
|
|
||||||
|
var provider = CreateProvider();
|
||||||
|
var context = CreateProviderContext(type);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
|
||||||
|
Assert.Equal(expected, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddsTempDataPropertyFilter_ForTempDataAttributeProperties()
|
public void AddsTempDataPropertyFilter_ForTempDataAttributeProperties()
|
||||||
{
|
{
|
||||||
|
|
@ -114,18 +131,9 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CompiledPageActionDescriptor CreateDescriptor(Type type)
|
|
||||||
{
|
|
||||||
return new CompiledPageActionDescriptor(new PageActionDescriptor())
|
|
||||||
{
|
|
||||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
||||||
HandlerTypeInfo = type.GetTypeInfo(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TempDataFilterPageApplicationModelProvider CreateProvider()
|
private static TempDataFilterPageApplicationModelProvider CreateProvider()
|
||||||
{
|
{
|
||||||
var tempDataSerializer = Mock.Of<TempDataSerializer>(s => s.CanSerializeType(It.IsAny<Type>()) == true);
|
var tempDataSerializer = new DefaultTempDataSerializer();
|
||||||
return new TempDataFilterPageApplicationModelProvider(tempDataSerializer);
|
return new TempDataFilterPageApplicationModelProvider(tempDataSerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,5 +168,17 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
[TempData]
|
[TempData]
|
||||||
public string Test { get; private set; }
|
public string Test { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TestPageModel_InvalidProperties
|
||||||
|
{
|
||||||
|
[TempData]
|
||||||
|
public ModelStateDictionary ModelState { get; set; }
|
||||||
|
|
||||||
|
[TempData]
|
||||||
|
public int SomeProperty { get; set; }
|
||||||
|
|
||||||
|
[TempData]
|
||||||
|
public TimeZoneInfo TimeZone { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure;
|
||||||
|
|
@ -86,6 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
||||||
Type type)
|
Type type)
|
||||||
{
|
{
|
||||||
List<LifecycleProperty> results = null;
|
List<LifecycleProperty> results = null;
|
||||||
|
var errorMessages = new List<string>();
|
||||||
|
|
||||||
var propertyHelpers = PropertyHelper.GetVisibleProperties(type: type);
|
var propertyHelpers = PropertyHelper.GetVisibleProperties(type: type);
|
||||||
for (var i = 0; i < propertyHelpers.Length; i++)
|
for (var i = 0; i < propertyHelpers.Length; i++)
|
||||||
|
|
@ -93,9 +95,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
||||||
var propertyHelper = propertyHelpers[i];
|
var propertyHelper = propertyHelpers[i];
|
||||||
var property = propertyHelper.Property;
|
var property = propertyHelper.Property;
|
||||||
var tempDataAttribute = property.GetCustomAttribute<TempDataAttribute>();
|
var tempDataAttribute = property.GetCustomAttribute<TempDataAttribute>();
|
||||||
if (tempDataAttribute != null)
|
if (tempDataAttribute != null && ValidateProperty(tempDataSerializer, errorMessages, propertyHelper.Property))
|
||||||
{
|
{
|
||||||
ValidateProperty(tempDataSerializer, propertyHelper.Property);
|
|
||||||
if (results == null)
|
if (results == null)
|
||||||
{
|
{
|
||||||
results = new List<LifecycleProperty>();
|
results = new List<LifecycleProperty>();
|
||||||
|
|
@ -111,28 +112,41 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (errorMessages.Count > 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Join(Environment.NewLine, errorMessages));
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ValidateProperty(TempDataSerializer tempDataSerializer, PropertyInfo property)
|
private static bool ValidateProperty(TempDataSerializer tempDataSerializer, List<string> errorMessages, PropertyInfo property)
|
||||||
{
|
{
|
||||||
if (!(property.SetMethod != null &&
|
if (!(property.SetMethod != null &&
|
||||||
property.SetMethod.IsPublic &&
|
property.SetMethod.IsPublic &&
|
||||||
property.GetMethod != null &&
|
property.GetMethod != null &&
|
||||||
property.GetMethod.IsPublic))
|
property.GetMethod.IsPublic))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
errorMessages.Add(
|
||||||
Resources.FormatTempDataProperties_PublicGetterSetter(property.DeclaringType.FullName, property.Name, nameof(TempDataAttribute)));
|
Resources.FormatTempDataProperties_PublicGetterSetter(property.DeclaringType.FullName, property.Name, nameof(TempDataAttribute)));
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tempDataSerializer.CanSerializeType(property.PropertyType))
|
if (!tempDataSerializer.CanSerializeType(property.PropertyType))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(Resources.FormatTempDataProperties_InvalidType(
|
var errorMessage = Resources.FormatTempDataProperties_InvalidType(
|
||||||
tempDataSerializer.GetType().FullName,
|
tempDataSerializer.GetType().FullName,
|
||||||
TypeNameHelper.GetTypeDisplayName(property.DeclaringType),
|
TypeNameHelper.GetTypeDisplayName(property.DeclaringType),
|
||||||
property.Name,
|
property.Name,
|
||||||
TypeNameHelper.GetTypeDisplayName(property.PropertyType)));
|
TypeNameHelper.GetTypeDisplayName(property.PropertyType));
|
||||||
|
|
||||||
|
errorMessages.Add(errorMessage);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
// 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.Buffers;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
||||||
|
{
|
||||||
|
// Note: this is currently an internal class that will be replaced with a shared version.
|
||||||
|
internal sealed class ArrayBufferWriter<T> : IBufferWriter<T>, IDisposable
|
||||||
|
{
|
||||||
|
private T[] _rentedBuffer;
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
private const int MinimumBufferSize = 256;
|
||||||
|
|
||||||
|
public ArrayBufferWriter()
|
||||||
|
{
|
||||||
|
_rentedBuffer = ArrayPool<T>.Shared.Rent(MinimumBufferSize);
|
||||||
|
_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayBufferWriter(int initialCapacity)
|
||||||
|
{
|
||||||
|
if (initialCapacity <= 0)
|
||||||
|
throw new ArgumentException(nameof(initialCapacity));
|
||||||
|
|
||||||
|
_rentedBuffer = ArrayPool<T>.Shared.Rent(initialCapacity);
|
||||||
|
_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyMemory<T> WrittenMemory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
|
return _rentedBuffer.AsMemory(0, _index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int WrittenCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
|
return _index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Capacity
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
|
return _rentedBuffer.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FreeCapacity
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
|
return _rentedBuffer.Length - _index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
|
ClearHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearHelper()
|
||||||
|
{
|
||||||
|
Debug.Assert(_rentedBuffer != null);
|
||||||
|
|
||||||
|
_rentedBuffer.AsSpan(0, _index).Clear();
|
||||||
|
_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the rented buffer back to the pool
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_rentedBuffer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearHelper();
|
||||||
|
ArrayPool<T>.Shared.Return(_rentedBuffer);
|
||||||
|
_rentedBuffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckIfDisposed()
|
||||||
|
{
|
||||||
|
if (_rentedBuffer == null)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(ArrayBufferWriter<T>));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Advance(int count)
|
||||||
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
|
if (count < 0)
|
||||||
|
throw new ArgumentException(nameof(count));
|
||||||
|
|
||||||
|
if (_index > _rentedBuffer.Length - count)
|
||||||
|
ThrowInvalidOperationException(_rentedBuffer.Length);
|
||||||
|
|
||||||
|
_index += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Memory<T> GetMemory(int sizeHint = 0)
|
||||||
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
|
CheckAndResizeBuffer(sizeHint);
|
||||||
|
return _rentedBuffer.AsMemory(_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<T> GetSpan(int sizeHint = 0)
|
||||||
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
|
CheckAndResizeBuffer(sizeHint);
|
||||||
|
return _rentedBuffer.AsSpan(_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckAndResizeBuffer(int sizeHint)
|
||||||
|
{
|
||||||
|
Debug.Assert(_rentedBuffer != null);
|
||||||
|
|
||||||
|
if (sizeHint < 0)
|
||||||
|
throw new ArgumentException(nameof(sizeHint));
|
||||||
|
|
||||||
|
if (sizeHint == 0)
|
||||||
|
{
|
||||||
|
sizeHint = MinimumBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int availableSpace = _rentedBuffer.Length - _index;
|
||||||
|
|
||||||
|
if (sizeHint > availableSpace)
|
||||||
|
{
|
||||||
|
int growBy = Math.Max(sizeHint, _rentedBuffer.Length);
|
||||||
|
|
||||||
|
int newSize = checked(_rentedBuffer.Length + growBy);
|
||||||
|
|
||||||
|
T[] oldBuffer = _rentedBuffer;
|
||||||
|
|
||||||
|
_rentedBuffer = ArrayPool<T>.Shared.Rent(newSize);
|
||||||
|
|
||||||
|
Debug.Assert(oldBuffer.Length >= _index);
|
||||||
|
Debug.Assert(_rentedBuffer.Length >= _index);
|
||||||
|
|
||||||
|
Span<T> previousBuffer = oldBuffer.AsSpan(0, _index);
|
||||||
|
previousBuffer.CopyTo(_rentedBuffer);
|
||||||
|
previousBuffer.Clear();
|
||||||
|
ArrayPool<T>.Shared.Return(oldBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(_rentedBuffer.Length - _index > 0);
|
||||||
|
Debug.Assert(_rentedBuffer.Length - _index >= sizeHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ThrowInvalidOperationException(int capacity)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Cannot advance past the end of the buffer, which has a size of {capacity}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,30 +3,157 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using System.Globalization;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
||||||
{
|
{
|
||||||
internal class DefaultTempDataSerializer : TempDataSerializer
|
internal class DefaultTempDataSerializer : TempDataSerializer
|
||||||
{
|
{
|
||||||
public override IDictionary<string, object> Deserialize(byte[] unprotectedData)
|
public override IDictionary<string, object> Deserialize(byte[] value)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(Core.Resources.FormatReferenceToNewtonsoftJsonRequired(
|
if (value == null)
|
||||||
Resources.DeserializingTempData,
|
{
|
||||||
"Microsoft.AspNetCore.Mvc.NewtonsoftJson",
|
throw new ArgumentNullException(nameof(value));
|
||||||
nameof(IMvcBuilder),
|
}
|
||||||
"AddNewtonsoftJson",
|
|
||||||
"ConfigureServices(...)"));
|
if (value.Length == 0)
|
||||||
|
{
|
||||||
|
return new Dictionary<string, object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
using var jsonDocument = JsonDocument.Parse(value);
|
||||||
|
var rootElement = jsonDocument.RootElement;
|
||||||
|
return DeserializeDictionary(rootElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDictionary<string, object> DeserializeDictionary(JsonElement rootElement)
|
||||||
|
{
|
||||||
|
var deserialized = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var item in rootElement.EnumerateObject())
|
||||||
|
{
|
||||||
|
object deserializedValue;
|
||||||
|
switch (item.Value.Type)
|
||||||
|
{
|
||||||
|
case JsonValueType.False:
|
||||||
|
case JsonValueType.True:
|
||||||
|
deserializedValue = item.Value.GetBoolean();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JsonValueType.Number:
|
||||||
|
deserializedValue = item.Value.GetInt32();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JsonValueType.String:
|
||||||
|
var stringValue = item.Value.GetString();
|
||||||
|
// BsonTempDataSerializer will parse certain types of string values. We'll attempt to imitiate it.
|
||||||
|
if (DateTime.TryParseExact(stringValue, "r", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime))
|
||||||
|
{
|
||||||
|
deserializedValue = dateTime;
|
||||||
|
}
|
||||||
|
else if (Guid.TryParseExact(stringValue, "B", out var guid))
|
||||||
|
{
|
||||||
|
deserializedValue = guid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deserializedValue = stringValue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JsonValueType.Null:
|
||||||
|
deserializedValue = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException(Resources.FormatTempData_CannotDeserializeType(item.Value.Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialized[item.Name] = deserializedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return deserialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override byte[] Serialize(IDictionary<string, object> values)
|
public override byte[] Serialize(IDictionary<string, object> values)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(Core.Resources.FormatReferenceToNewtonsoftJsonRequired(
|
if (values == null || values.Count == 0)
|
||||||
Resources.SerializingTempData,
|
{
|
||||||
"Microsoft.AspNetCore.Mvc.NewtonsoftJson",
|
return Array.Empty<byte>();
|
||||||
nameof(IMvcBuilder),
|
}
|
||||||
"AddNewtonsoftJson",
|
|
||||||
"ConfigureServices(...)"));
|
using (var bufferWriter = new ArrayBufferWriter<byte>())
|
||||||
|
{
|
||||||
|
var writer = new Utf8JsonWriter(bufferWriter);
|
||||||
|
writer.WriteStartObject();
|
||||||
|
foreach (var (key, value) in values)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
writer.WriteNull(key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to allow only simple types to be serialized.
|
||||||
|
if (!CanSerializeType(value.GetType()))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
Resources.FormatTempData_CannotSerializeType(
|
||||||
|
typeof(DefaultTempDataSerializer).FullName,
|
||||||
|
value.GetType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case Enum _:
|
||||||
|
writer.WriteNumber(key, (int)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case string stringValue:
|
||||||
|
writer.WriteString(key, stringValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case int intValue:
|
||||||
|
writer.WriteNumber(key, intValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bool boolValue:
|
||||||
|
writer.WriteBoolean(key, boolValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DateTime dateTime:
|
||||||
|
writer.WriteString(key, dateTime.ToString("r", CultureInfo.InvariantCulture));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Guid guid:
|
||||||
|
writer.WriteString(key, guid.ToString("B", CultureInfo.InvariantCulture));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.WriteEndObject();
|
||||||
|
writer.Flush();
|
||||||
|
|
||||||
|
return bufferWriter.WrittenMemory.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanSerializeType(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
type = Nullable.GetUnderlyingType(type) ?? type;
|
||||||
|
|
||||||
|
return
|
||||||
|
type.IsEnum ||
|
||||||
|
type == typeof(int) ||
|
||||||
|
type == typeof(string) ||
|
||||||
|
type == typeof(bool) ||
|
||||||
|
type == typeof(DateTime) ||
|
||||||
|
type == typeof(Guid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -808,6 +808,34 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
internal static string FormatSerializingTempData()
|
internal static string FormatSerializingTempData()
|
||||||
=> GetString("SerializingTempData");
|
=> GetString("SerializingTempData");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The '{0}' cannot serialize an object of type '{1}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TempData_CannotSerializeType
|
||||||
|
{
|
||||||
|
get => GetString("TempData_CannotSerializeType");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The '{0}' cannot serialize an object of type '{1}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatTempData_CannotSerializeType(object p0, object p1)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotSerializeType"), p0, p1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsupported data type '{0}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TempData_CannotDeserializeType
|
||||||
|
{
|
||||||
|
get => GetString("TempData_CannotDeserializeType");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsupported data type '{0}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatTempData_CannotDeserializeType(object p0)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotDeserializeType"), p0);
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -289,4 +289,10 @@
|
||||||
<data name="SerializingTempData" xml:space="preserve">
|
<data name="SerializingTempData" xml:space="preserve">
|
||||||
<value>Serializing TempDataDictionary</value>
|
<value>Serializing TempDataDictionary</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TempData_CannotSerializeType" xml:space="preserve">
|
||||||
|
<value>The '{0}' cannot serialize an object of type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TempData_CannotDeserializeType" xml:space="preserve">
|
||||||
|
<value>Unsupported data type '{0}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Moq;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
||||||
|
|
@ -48,20 +47,20 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddsTempDataPropertyFilter_ForTempDataAttributeProperties()
|
public void OnProvidersExecuting_ThrowsIfThePropertyTypeIsUnsupported()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var type = typeof(TestController_NullableNonPrimitiveTempDataProperty);
|
var type = typeof(TestController_InvalidProperties);
|
||||||
|
var expected = $"TempData serializer '{typeof(DefaultTempDataSerializer)}' cannot serialize property '{type}.ModelState' of type '{typeof(ModelStateDictionary)}'." +
|
||||||
|
Environment.NewLine +
|
||||||
|
$"TempData serializer '{typeof(DefaultTempDataSerializer)}' cannot serialize property '{type}.TimeZone' of type '{typeof(TimeZoneInfo)}'.";
|
||||||
var provider = CreateProvider();
|
var provider = CreateProvider();
|
||||||
|
|
||||||
var context = GetContext(type);
|
var context = GetContext(type);
|
||||||
|
|
||||||
// Act
|
// Act & Assert
|
||||||
provider.OnProvidersExecuting(context);
|
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
|
||||||
|
Assert.Equal(expected, ex.Message);
|
||||||
// Assert
|
|
||||||
var controller = Assert.Single(context.Result.Controllers);
|
|
||||||
Assert.IsType<ControllerSaveTempDataPropertyFilterFactory>(Assert.Single(controller.Filters));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -109,7 +108,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
||||||
|
|
||||||
private static TempDataApplicationModelProvider CreateProvider()
|
private static TempDataApplicationModelProvider CreateProvider()
|
||||||
{
|
{
|
||||||
var tempDataSerializer = Mock.Of<TempDataSerializer>(s => s.CanSerializeType(It.IsAny<Type>()) == true);
|
var tempDataSerializer = new DefaultTempDataSerializer();
|
||||||
return new TempDataApplicationModelProvider(tempDataSerializer);
|
return new TempDataApplicationModelProvider(tempDataSerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,12 +128,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
||||||
public DateTime? DateTime { get; set; }
|
public DateTime? DateTime { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestController_NullableNonPrimitiveTempDataProperty
|
|
||||||
{
|
|
||||||
[TempData]
|
|
||||||
public DateTime? DateTime { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestController_OneTempDataProperty
|
public class TestController_OneTempDataProperty
|
||||||
{
|
{
|
||||||
public string Test { get; set; }
|
public string Test { get; set; }
|
||||||
|
|
@ -148,5 +141,17 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
|
||||||
[TempData]
|
[TempData]
|
||||||
public string Test { get; private set; }
|
public string Test { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TestController_InvalidProperties
|
||||||
|
{
|
||||||
|
[TempData]
|
||||||
|
public ModelStateDictionary ModelState { get; set; }
|
||||||
|
|
||||||
|
[TempData]
|
||||||
|
public int SomeProperty { get; set; }
|
||||||
|
|
||||||
|
[TempData]
|
||||||
|
public TimeZoneInfo TimeZone { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
// 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.Generic;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
||||||
|
{
|
||||||
|
public class DefaultTempDataSerializerTest : TempDataSerializerTestBase
|
||||||
|
{
|
||||||
|
protected override TempDataSerializer GetTempDataSerializer() => new DefaultTempDataSerializer();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_StringThatLooksLikeCompliantDateTime()
|
||||||
|
{
|
||||||
|
// This is an unintentional side-effect of trying to support a compat with JSON.NET.
|
||||||
|
// Any string that looks like a compliant DateTime object will be parsed as a DateTime.
|
||||||
|
// This test documents this behavior.
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var value = new DateTime(2009, 1, 1, 12, 37, 43);
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value.ToString("r") }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<DateTime>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_StringThatIsNotCompliantDateTime()
|
||||||
|
{
|
||||||
|
// This is an unintentional side-effect of trying to support a compat with JSON.NET.
|
||||||
|
// Any string that looks like a compliant DateTime object will be parsed as a DateTime.
|
||||||
|
// This test documents this behavior.
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var value = new DateTime(2009, 1, 1, 12, 37, 43);
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value.ToString() }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<string>(values[key]);
|
||||||
|
Assert.Equal(value.ToString(), roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_StringThatIsNotCompliantGuid()
|
||||||
|
{
|
||||||
|
// This is an unintentional side-effect of trying to support a compat with JSON.NET.
|
||||||
|
// Any string that looks like a compliant DateTime object will be parsed as a DateTime.
|
||||||
|
// This test documents this behavior.
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var value = Guid.NewGuid();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value.ToString() }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<string>(values[key]);
|
||||||
|
Assert.Equal(value.ToString(), roundTripValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
// 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.Generic;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
||||||
|
{
|
||||||
|
public abstract class TempDataSerializerTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void DeserializeTempData_ReturnsEmptyDictionary_DataIsEmpty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var serializer = GetTempDataSerializer();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tempDataDictionary = serializer.Deserialize(new byte[0]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(tempDataDictionary);
|
||||||
|
Assert.Empty(tempDataDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_NullValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "NullKey";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, null }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(values.ContainsKey(key));
|
||||||
|
Assert.Null(values[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(-10)]
|
||||||
|
[InlineData(3340)]
|
||||||
|
[InlineData(int.MaxValue)]
|
||||||
|
[InlineData(int.MinValue)]
|
||||||
|
public void RoundTripTest_IntValue(int value)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<int>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null)]
|
||||||
|
[InlineData(10)]
|
||||||
|
public void RoundTripTest_NullableInt(int? value)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = (int?)values[key];
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_StringValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var value = "test-value";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<string>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_Enum()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var value = DayOfWeek.Friday;
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = (DayOfWeek)values[key];
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_DateTimeValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var value = new DateTime(2009, 1, 1, 12, 37, 43);
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<DateTime>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoundTripTest_GuidValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var key = "test-key";
|
||||||
|
var testProvider = GetTempDataSerializer();
|
||||||
|
var value = Guid.NewGuid();
|
||||||
|
var input = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ key, value }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var bytes = testProvider.Serialize(input);
|
||||||
|
var values = testProvider.Deserialize(bytes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var roundTripValue = Assert.IsType<Guid>(values[key]);
|
||||||
|
Assert.Equal(value, roundTripValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract TempDataSerializer GetTempDataSerializer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -507,7 +507,7 @@ Products: Book1, Book2 (1)";
|
||||||
|
|
||||||
// Act - 3
|
// Act - 3
|
||||||
// Trigger an expiration of the nested content.
|
// Trigger an expiration of the nested content.
|
||||||
var content = @"[{ productName: ""Music Systems"" },{ productName: ""Televisions"" }]";
|
var content = @"[{ ""ProductName"": ""Music Systems"" },{ ""ProductName"": ""Televisions"" }]";
|
||||||
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/categories/Electronics");
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/categories/Electronics");
|
||||||
requestMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
|
requestMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
|
||||||
(await Client.SendAsync(requestMessage)).EnsureSuccessStatusCode();
|
(await Client.SendAsync(requestMessage)).EnsureSuccessStatusCode();
|
||||||
|
|
|
||||||
|
|
@ -784,7 +784,7 @@ Hello from /Pages/WithViewStart/Index.cshtml!";
|
||||||
Assert.Equal(expected, content);
|
Assert.Equal(expected, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact(Skip = "https://github.com/dotnet/corefx/issues/36024")]
|
||||||
public async Task PolymorphicPropertiesOnPageModelsAreValidated()
|
public async Task PolymorphicPropertiesOnPageModelsAreValidated()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.Formatters.Xml" />
|
<Reference Include="Microsoft.AspNetCore.Mvc.Formatters.Xml" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
|
|
||||||
|
|
||||||
<Reference Include="Microsoft.AspNetCore.Cors" />
|
<Reference Include="Microsoft.AspNetCore.Cors" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ namespace CorsWebSite
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddControllers(ConfigureMvcOptions)
|
services.AddControllers(ConfigureMvcOptions)
|
||||||
.AddNewtonsoftJson()
|
|
||||||
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
||||||
services.Configure<CorsOptions>(options =>
|
services.Configure<CorsOptions>(options =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.Formatters.Xml" />
|
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
|
|
||||||
|
|
||||||
<Reference Include="Microsoft.AspNetCore.Localization.Routing" />
|
<Reference Include="Microsoft.AspNetCore.Localization.Routing" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
|
@ -24,9 +21,7 @@ namespace GenericHostWebSite
|
||||||
// Remove when all URL generation tests are passing - https://github.com/aspnet/Routing/issues/590
|
// Remove when all URL generation tests are passing - https://github.com/aspnet/Routing/issues/590
|
||||||
options.EnableEndpointRouting = false;
|
options.EnableEndpointRouting = false;
|
||||||
})
|
})
|
||||||
.SetCompatibilityVersion(CompatibilityVersion.Latest)
|
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
||||||
.AddNewtonsoftJson()
|
|
||||||
.AddXmlDataContractSerializerFormatters();
|
|
||||||
|
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
|
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
|
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
|
||||||
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
|
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ namespace HtmlGenerationWebSite
|
||||||
// null which is interpreted as true unless element includes an action attribute.
|
// null which is interpreted as true unless element includes an action attribute.
|
||||||
services.AddMvc(ConfigureMvcOptions)
|
services.AddMvc(ConfigureMvcOptions)
|
||||||
.InitializeTagHelper<FormTagHelper>((helper, _) => helper.Antiforgery = false)
|
.InitializeTagHelper<FormTagHelper>((helper, _) => helper.Antiforgery = false)
|
||||||
.AddNewtonsoftJson()
|
|
||||||
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
||||||
|
|
||||||
services.AddSingleton(typeof(ISignalTokenProviderService<>), typeof(SignalTokenProviderService<>));
|
services.AddSingleton(typeof(ISignalTokenProviderService<>), typeof(SignalTokenProviderService<>));
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ namespace RazorPagesWebSite
|
||||||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => options.LoginPath = "/Login");
|
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => options.LoginPath = "/Login");
|
||||||
services.AddMvc(options => options.EnableEndpointRouting = false)
|
services.AddMvc(options => options.EnableEndpointRouting = false)
|
||||||
.AddMvcLocalization()
|
.AddMvcLocalization()
|
||||||
.AddNewtonsoftJson()
|
|
||||||
.AddRazorPagesOptions(options =>
|
.AddRazorPagesOptions(options =>
|
||||||
{
|
{
|
||||||
options.Conventions.AuthorizePage("/HelloWorldWithAuth");
|
options.Conventions.AuthorizePage("/HelloWorldWithAuth");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue