Fix [TempData] for PageModel's
This commit is contained in:
parent
1a8ac88da7
commit
59a3aade9b
|
|
@ -81,6 +81,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
// Action executors
|
// Action executors
|
||||||
services.TryAddSingleton<PageResultExecutor>();
|
services.TryAddSingleton<PageResultExecutor>();
|
||||||
services.TryAddSingleton<RedirectToPageResultExecutor>();
|
services.TryAddSingleton<RedirectToPageResultExecutor>();
|
||||||
|
|
||||||
|
services.TryAddTransient<PageSaveTempDataPropertyFilter>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,28 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class RazorPagesOptionsExtensions
|
public static class RazorPagesOptionsExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures the specified <paramref name="factory"/> to apply filters to all Razor Pages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The <see cref="RazorPagesOptions"/> to configure.</param>
|
||||||
|
/// <param name="factory">The factory to create filters.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static RazorPagesOptions ConfigureFilter(this RazorPagesOptions options, Func<PageApplicationModel, IFilterMetadata> factory)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (factory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(factory));
|
||||||
|
}
|
||||||
|
|
||||||
|
options.Conventions.Add(new FolderConvention("/", model => model.Filters.Add(factory(model))));
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configures the specified <paramref name="filter"/> to apply to all Razor Pages.
|
/// Configures the specified <paramref name="filter"/> to apply to all Razor Pages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -346,10 +346,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a workaround for not yet having proper filter for Pages.
|
// This is a workaround for not yet having proper filter for Pages.
|
||||||
SaveTempDataPropertyFilter propertyFilter = null;
|
PageSaveTempDataPropertyFilter propertyFilter = null;
|
||||||
for (var i = 0; i < _filters.Length; i++)
|
for (var i = 0; i < _filters.Length; i++)
|
||||||
{
|
{
|
||||||
propertyFilter = _filters[i] as SaveTempDataPropertyFilter;
|
propertyFilter = _filters[i] as PageSaveTempDataPropertyFilter;
|
||||||
if (propertyFilter != null)
|
if (propertyFilter != null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
|
@ -358,7 +358,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
if (propertyFilter != null)
|
if (propertyFilter != null)
|
||||||
{
|
{
|
||||||
propertyFilter.Subject = _page;
|
object subject = _page;
|
||||||
|
|
||||||
|
if (_model != null)
|
||||||
|
{
|
||||||
|
subject = _model;
|
||||||
|
}
|
||||||
|
|
||||||
|
propertyFilter.Subject = subject;
|
||||||
propertyFilter.ApplyTempDataChanges(_pageContext.HttpContext);
|
propertyFilter.ApplyTempDataChanges(_pageContext.HttpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support for [TempData] on properties
|
// Support for [TempData] on properties
|
||||||
options.ConfigureFilter(new SaveTempDataPropertyFilterFactory());
|
options.ConfigureFilter(page => new PageSaveTempDataPropertyFilterFactory());
|
||||||
|
|
||||||
// Always require an antiforgery token on post
|
// Always require an antiforgery token on post
|
||||||
options.ConfigureFilter(new AutoValidateAntiforgeryTokenAttribute());
|
options.ConfigureFilter(new AutoValidateAntiforgeryTokenAttribute());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IApplicationModelProvider, TempDataApplicationModelProvider>());
|
ServiceDescriptor.Transient<IApplicationModelProvider, TempDataApplicationModelProvider>());
|
||||||
services.TryAddSingleton<SaveTempDataFilter>();
|
services.TryAddSingleton<SaveTempDataFilter>();
|
||||||
services.TryAddTransient<SaveTempDataPropertyFilter>();
|
|
||||||
|
|
||||||
|
services.TryAddTransient<ControllerSaveTempDataPropertyFilter>();
|
||||||
|
|
||||||
// This does caching so it should stay singleton
|
// This does caching so it should stay singleton
|
||||||
services.TryAddSingleton<ITempDataProvider, CookieTempDataProvider>();
|
services.TryAddSingleton<ITempDataProvider, CookieTempDataProvider>();
|
||||||
|
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
// 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 System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|
||||||
{
|
|
||||||
public class SaveTempDataPropertyFilter : ISaveTempDataCallback, IActionFilter
|
|
||||||
{
|
|
||||||
private const string Prefix = "TempDataProperty-";
|
|
||||||
private readonly ITempDataDictionaryFactory _factory;
|
|
||||||
|
|
||||||
public SaveTempDataPropertyFilter(ITempDataDictionaryFactory factory)
|
|
||||||
{
|
|
||||||
_factory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot be public as <c>PropertyHelper</c> is an internal shared source type
|
|
||||||
internal IList<PropertyHelper> PropertyHelpers { get; set; }
|
|
||||||
|
|
||||||
public object Subject { get; set; }
|
|
||||||
|
|
||||||
public IDictionary<PropertyInfo, object> OriginalValues { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Puts the modified values of <see cref="Subject"/> into <paramref name="tempData"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tempData">The <see cref="ITempDataDictionary"/> to be updated.</param>
|
|
||||||
public void OnTempDataSaving(ITempDataDictionary tempData)
|
|
||||||
{
|
|
||||||
if (Subject != null && OriginalValues != null)
|
|
||||||
{
|
|
||||||
foreach (var kvp in OriginalValues)
|
|
||||||
{
|
|
||||||
var property = kvp.Key;
|
|
||||||
var originalValue = kvp.Value;
|
|
||||||
|
|
||||||
var newValue = property.GetValue(Subject);
|
|
||||||
if (newValue != null && !newValue.Equals(originalValue))
|
|
||||||
{
|
|
||||||
tempData[Prefix + property.Name] = newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies values from TempData from <paramref name="httpContext"/> to the <see cref="Subject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="httpContext">The <see cref="HttpContext"/> used to find TempData.</param>
|
|
||||||
public void ApplyTempDataChanges(HttpContext httpContext)
|
|
||||||
{
|
|
||||||
if (Subject == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(Subject));
|
|
||||||
}
|
|
||||||
|
|
||||||
var tempData = _factory.GetTempData(httpContext);
|
|
||||||
|
|
||||||
if (OriginalValues == null)
|
|
||||||
{
|
|
||||||
OriginalValues = new Dictionary<PropertyInfo, object>();
|
|
||||||
}
|
|
||||||
|
|
||||||
SetPropertyVaules(tempData, Subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void OnActionExecuting(ActionExecutingContext context)
|
|
||||||
{
|
|
||||||
Subject = context.Controller;
|
|
||||||
var tempData = _factory.GetTempData(context.HttpContext);
|
|
||||||
|
|
||||||
OriginalValues = new Dictionary<PropertyInfo, object>();
|
|
||||||
|
|
||||||
SetPropertyVaules(tempData, Subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void OnActionExecuted(ActionExecutedContext context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPropertyVaules(ITempDataDictionary tempData, object subject)
|
|
||||||
{
|
|
||||||
if (PropertyHelpers == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < PropertyHelpers.Count; i++)
|
|
||||||
{
|
|
||||||
var property = PropertyHelpers[i];
|
|
||||||
var value = tempData[Prefix + property.Name];
|
|
||||||
|
|
||||||
OriginalValues[property.Property] = value;
|
|
||||||
|
|
||||||
var propertyTypeInfo = property.Property.PropertyType.GetTypeInfo();
|
|
||||||
|
|
||||||
var isReferenceTypeOrNullable = !propertyTypeInfo.IsValueType || Nullable.GetUnderlyingType(property.GetType()) != null;
|
|
||||||
if (value != null || isReferenceTypeOrNullable)
|
|
||||||
{
|
|
||||||
property.SetValue(subject, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
{
|
||||||
|
public class ControllerSaveTempDataPropertyFilter : SaveTempDataPropertyFilterBase, IActionFilter
|
||||||
|
{
|
||||||
|
public ControllerSaveTempDataPropertyFilter(ITempDataDictionaryFactory factory)
|
||||||
|
: base(factory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnActionExecuted(ActionExecutedContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnActionExecuting(ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
Subject = context.Controller;
|
||||||
|
var tempData = _factory.GetTempData(context.HttpContext);
|
||||||
|
|
||||||
|
SetPropertyVaules(tempData, Subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -5,14 +5,12 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
{
|
{
|
||||||
public class SaveTempDataPropertyFilterFactory : IFilterFactory
|
public class ControllerSaveTempDataPropertyFilterFactory : IFilterFactory
|
||||||
{
|
{
|
||||||
// Cannot be public as <c>PropertyHelper</c> is an internal shared source type
|
public IList<TempDataProperty> TempDataProperties { get; set; }
|
||||||
internal IList<PropertyHelper> TempDataProperties { get; set; }
|
|
||||||
|
|
||||||
public bool IsReusable => false;
|
public bool IsReusable => false;
|
||||||
|
|
||||||
|
|
@ -23,8 +21,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
throw new ArgumentNullException(nameof(serviceProvider));
|
throw new ArgumentNullException(nameof(serviceProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
var service = serviceProvider.GetRequiredService<SaveTempDataPropertyFilter>();
|
var service = serviceProvider.GetRequiredService<ControllerSaveTempDataPropertyFilter>();
|
||||||
service.PropertyHelpers = TempDataProperties;
|
service.TempDataProperties = TempDataProperties;
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
{
|
||||||
|
public class PageSaveTempDataPropertyFilter : SaveTempDataPropertyFilterBase
|
||||||
|
{
|
||||||
|
public PageSaveTempDataPropertyFilter(ITempDataDictionaryFactory factory)
|
||||||
|
: base(factory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageSaveTempDataPropertyFilterFactory FilterFactory { get; set; }
|
||||||
|
|
||||||
|
public override object Subject {
|
||||||
|
get => base.Subject;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.Subject = value;
|
||||||
|
SetTempDataProperties(value.GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTempDataProperties(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FilterFactory == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
Resources.FormatPropertyOfTypeCannotBeNull(
|
||||||
|
nameof(FilterFactory),
|
||||||
|
typeof(PageSaveTempDataPropertyFilter).Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
TempDataProperties = FilterFactory.GetTempDataProperties(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies values from TempData from <paramref name="httpContext"/> to the
|
||||||
|
/// <see cref="SaveTempDataPropertyFilterBase.Subject"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext">The <see cref="HttpContext"/> used to find TempData.</param>
|
||||||
|
public void ApplyTempDataChanges(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
if (Subject == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
Resources.FormatPropertyOfTypeCannotBeNull(
|
||||||
|
nameof(Subject),
|
||||||
|
typeof(PageSaveTempDataPropertyFilter).Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempData = _factory.GetTempData(httpContext);
|
||||||
|
|
||||||
|
SetPropertyVaules(tempData, Subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
{
|
||||||
|
public class PageSaveTempDataPropertyFilterFactory : IFilterFactory
|
||||||
|
{
|
||||||
|
public IList<TempDataProperty> TempDataProperties { get; set; }
|
||||||
|
|
||||||
|
public bool IsReusable => false;
|
||||||
|
|
||||||
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
if (serviceProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(serviceProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
var service = serviceProvider.GetRequiredService<PageSaveTempDataPropertyFilter>();
|
||||||
|
service.FilterFactory = this;
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<TempDataProperty> GetTempDataProperties(Type modelType)
|
||||||
|
{
|
||||||
|
// TempDataProperties are stored here as a cache for the filter. But in pages by the time we know the type
|
||||||
|
// of our model we no longer have access to the factory, so we store the factory on the filter so it can
|
||||||
|
// call this method to populate its TempDataProperties.
|
||||||
|
if (TempDataProperties == null)
|
||||||
|
{
|
||||||
|
TempDataProperties = SaveTempDataPropertyFilterBase.GetTempDataProperties(modelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TempDataProperties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
// 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 System.Reflection;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
{
|
||||||
|
public abstract class SaveTempDataPropertyFilterBase : ISaveTempDataCallback
|
||||||
|
{
|
||||||
|
protected const string Prefix = "TempDataProperty-";
|
||||||
|
|
||||||
|
protected readonly ITempDataDictionaryFactory _factory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes the temp data properties which exist on <see cref="Subject"/>
|
||||||
|
/// </summary>
|
||||||
|
public IList<TempDataProperty> TempDataProperties { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="object"/> which has the temp data properties.
|
||||||
|
/// </summary>
|
||||||
|
public virtual object Subject { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tracks the values which originally existed in temp data.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<PropertyInfo, object> OriginalValues { get; } = new Dictionary<PropertyInfo, object>();
|
||||||
|
|
||||||
|
public SaveTempDataPropertyFilterBase(ITempDataDictionaryFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Puts the modified values of <see cref="Subject"/> into <paramref name="tempData"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tempData">The <see cref="ITempDataDictionary"/> to be updated.</param>
|
||||||
|
public void OnTempDataSaving(ITempDataDictionary tempData)
|
||||||
|
{
|
||||||
|
if (Subject != null && OriginalValues != null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in OriginalValues)
|
||||||
|
{
|
||||||
|
var property = kvp.Key;
|
||||||
|
var originalValue = kvp.Value;
|
||||||
|
|
||||||
|
var newValue = property.GetValue(Subject);
|
||||||
|
if (newValue != null && !newValue.Equals(originalValue))
|
||||||
|
{
|
||||||
|
tempData[Prefix + property.Name] = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IList<TempDataProperty> GetTempDataProperties(Type type)
|
||||||
|
{
|
||||||
|
List<TempDataProperty> results = null;
|
||||||
|
|
||||||
|
var propertyHelpers = PropertyHelper.GetVisibleProperties(type: type);
|
||||||
|
|
||||||
|
for (var i = 0; i < propertyHelpers.Length; i++)
|
||||||
|
{
|
||||||
|
var propertyHelper = propertyHelpers[i];
|
||||||
|
if (propertyHelper.Property.IsDefined(typeof(TempDataAttribute)))
|
||||||
|
{
|
||||||
|
ValidateProperty(propertyHelper);
|
||||||
|
if (results == null)
|
||||||
|
{
|
||||||
|
results = new List<TempDataProperty>();
|
||||||
|
}
|
||||||
|
|
||||||
|
results.Add(new TempDataProperty(
|
||||||
|
propertyHelper.Property,
|
||||||
|
propertyHelper.GetValue,
|
||||||
|
propertyHelper.SetValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateProperty(PropertyHelper propertyHelper)
|
||||||
|
{
|
||||||
|
var property = propertyHelper.Property;
|
||||||
|
if (!(property.SetMethod != null &&
|
||||||
|
property.SetMethod.IsPublic &&
|
||||||
|
property.GetMethod != null &&
|
||||||
|
property.GetMethod.IsPublic))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
Resources.FormatTempDataProperties_PublicGetterSetter(property.DeclaringType.FullName, property.Name, nameof(TempDataAttribute)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(property.PropertyType.GetTypeInfo().IsPrimitive || property.PropertyType == typeof(string)))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
Resources.FormatTempDataProperties_PrimitiveTypeOrString(property.DeclaringType.FullName, property.Name, nameof(TempDataAttribute)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the values of the properties of <paramref name="subject"/> from <paramref name="tempData"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tempData">The <see cref="ITempDataDictionary"/> with the data to set on <paramref name="subject"/>.</param>
|
||||||
|
/// <param name="subject">The <see cref="object"/> which will have it's properties set.</param>
|
||||||
|
protected void SetPropertyVaules(ITempDataDictionary tempData, object subject)
|
||||||
|
{
|
||||||
|
if (TempDataProperties == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < TempDataProperties.Count; i++)
|
||||||
|
{
|
||||||
|
var property = TempDataProperties[i];
|
||||||
|
var value = tempData[Prefix + property.PropertyInfo.Name];
|
||||||
|
|
||||||
|
OriginalValues[property.PropertyInfo] = value;
|
||||||
|
|
||||||
|
var propertyTypeInfo = property.PropertyInfo.PropertyType.GetTypeInfo();
|
||||||
|
|
||||||
|
var isReferenceTypeOrNullable = !propertyTypeInfo.IsValueType || Nullable.GetUnderlyingType(property.GetType()) != null;
|
||||||
|
if (value != null || isReferenceTypeOrNullable)
|
||||||
|
{
|
||||||
|
property.SetValue(subject, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 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.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
{
|
||||||
|
public struct TempDataProperty
|
||||||
|
{
|
||||||
|
private Func<object, object> _getter;
|
||||||
|
|
||||||
|
private Action<object, object> _setter;
|
||||||
|
|
||||||
|
public TempDataProperty(PropertyInfo propertyInfo, Func<object, object> getter, Action<object, object> setter)
|
||||||
|
{
|
||||||
|
PropertyInfo = propertyInfo;
|
||||||
|
_getter = getter;
|
||||||
|
_setter = setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyInfo PropertyInfo { get; }
|
||||||
|
|
||||||
|
public object GetValue(object obj)
|
||||||
|
{
|
||||||
|
return _getter(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(object obj, object value)
|
||||||
|
{
|
||||||
|
_setter(obj, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,11 +2,8 @@
|
||||||
// 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 System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -31,50 +28,20 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
|
||||||
foreach (var controllerModel in context.Result.Controllers)
|
foreach (var controllerModel in context.Result.Controllers)
|
||||||
{
|
{
|
||||||
SaveTempDataPropertyFilterFactory factory = null;
|
|
||||||
var propertyHelpers = PropertyHelper.GetVisibleProperties(type: controllerModel.ControllerType.AsType());
|
var modelType = controllerModel.ControllerType.AsType();
|
||||||
for (var i = 0; i < propertyHelpers.Length; i++)
|
var tempDataProperties = SaveTempDataPropertyFilterBase.GetTempDataProperties(modelType);
|
||||||
|
|
||||||
|
if (tempDataProperties != null)
|
||||||
{
|
{
|
||||||
var propertyHelper = propertyHelpers[i];
|
var factory = new ControllerSaveTempDataPropertyFilterFactory()
|
||||||
if (propertyHelper.Property.IsDefined(typeof(TempDataAttribute)))
|
|
||||||
{
|
{
|
||||||
ValidateProperty(propertyHelper);
|
TempDataProperties = tempDataProperties
|
||||||
if (factory == null)
|
};
|
||||||
{
|
|
||||||
factory = new SaveTempDataPropertyFilterFactory()
|
|
||||||
{
|
|
||||||
TempDataProperties = new List<PropertyHelper>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
factory.TempDataProperties.Add(propertyHelper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (factory != null)
|
|
||||||
{
|
|
||||||
controllerModel.Filters.Add(factory);
|
controllerModel.Filters.Add(factory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateProperty(PropertyHelper propertyHelper)
|
|
||||||
{
|
|
||||||
var property = propertyHelper.Property;
|
|
||||||
if (!(property.SetMethod != null &&
|
|
||||||
property.SetMethod.IsPublic &&
|
|
||||||
property.GetMethod != null &&
|
|
||||||
property.GetMethod.IsPublic))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
Resources.FormatTempDataProperties_PublicGetterSetter(property.DeclaringType.FullName, property.Name, nameof(TempDataAttribute)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(property.PropertyType.GetTypeInfo().IsPrimitive || property.PropertyType == typeof(string)))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
Resources.FormatTempDataProperties_PrimitiveTypeOrString(property.DeclaringType.FullName, property.Name, nameof(TempDataAttribute)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var routeRequest = new HttpRequestMessage(HttpMethod.Get, "http://localhost/RouteData/pizza");
|
var routeRequest = new HttpRequestMessage(HttpMethod.Get, "http://localhost/RouteData/pizza");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var routeResponse = await Client.SendAsync(routeRequest);
|
var routeResponse = await Client.SendAsync(routeRequest);
|
||||||
|
|
||||||
|
|
@ -413,6 +413,69 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal("Hi2", content.Trim());
|
Assert.Equal("Hi2", content.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TempData_TempDataPropertyOnPageModel_IsPopulatedFromTempData()
|
||||||
|
{
|
||||||
|
// Arrange 1
|
||||||
|
var url = "http://localhost/TempData/SetMessageAndRedirect";
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
|
|
||||||
|
// Act 1
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert 1
|
||||||
|
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||||
|
|
||||||
|
// Act 2
|
||||||
|
request = new HttpRequestMessage(HttpMethod.Get, response.Headers.Location);
|
||||||
|
request.Headers.Add("Cookie", GetCookie(response));
|
||||||
|
response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert 2
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.StartsWith("Message: Secret Message", content.Trim());
|
||||||
|
Assert.EndsWith("TempData: Secret Message", content.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TempData_TempDataPropertyOnPageModel_PopulatesTempData()
|
||||||
|
{
|
||||||
|
// Arrange 1
|
||||||
|
var getRequest = new HttpRequestMessage(HttpMethod.Get, "http://localhost/TempData/TempDataPageModelProperty");
|
||||||
|
var getResponse = await Client.SendAsync(getRequest);
|
||||||
|
var getResponseBody = await getResponse.Content.ReadAsStringAsync();
|
||||||
|
var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(getResponseBody, "/TempData/TempDataPageModelProperty");
|
||||||
|
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
|
||||||
|
|
||||||
|
var url = "http://localhost/TempData/TempDataPageModelProperty";
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||||
|
request.Headers.Add("Cookie", cookie.Key + "=" + cookie.Value);
|
||||||
|
request.Headers.Add("RequestVerificationToken", formToken);
|
||||||
|
|
||||||
|
// Act 1
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert 1
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.StartsWith("Message: Secret post", content.Trim());
|
||||||
|
Assert.EndsWith("TempData:", content.Trim());
|
||||||
|
|
||||||
|
// Arrange 2
|
||||||
|
request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/TempData/TempDataPageModelProperty");
|
||||||
|
request.Headers.Add("Cookie", GetCookie(response));
|
||||||
|
|
||||||
|
// Act 2
|
||||||
|
response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert 2
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.StartsWith("Message: Secret post", content.Trim());
|
||||||
|
Assert.EndsWith("TempData: Secret post", content.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AuthorizePage_AddsAuthorizationForSpecificPages()
|
public async Task AuthorizePage_AddsAuthorizationForSpecificPages()
|
||||||
{
|
{
|
||||||
|
|
@ -432,7 +495,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var url = "/Pages/Admin/Login";
|
var url = "/Pages/Admin/Login";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response = await Client.GetAsync(url);
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
|
@ -677,7 +740,7 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP
|
||||||
Assert.Equal(expected, response.Headers.Location.ToString());
|
Assert.Equal(expected, response.Headers.Location.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RedirectToSelfWorks()
|
public async Task RedirectToSelfWorks()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
filterDescriptor =>
|
filterDescriptor =>
|
||||||
{
|
{
|
||||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||||
Assert.IsType<SaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
||||||
},
|
},
|
||||||
filterDescriptor =>
|
filterDescriptor =>
|
||||||
{
|
{
|
||||||
|
|
@ -343,7 +343,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
filterDescriptor =>
|
filterDescriptor =>
|
||||||
{
|
{
|
||||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||||
Assert.IsType<SaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
||||||
},
|
},
|
||||||
filterDescriptor =>
|
filterDescriptor =>
|
||||||
{
|
{
|
||||||
|
|
@ -396,7 +396,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
filterDescriptor =>
|
filterDescriptor =>
|
||||||
{
|
{
|
||||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||||
Assert.IsType<SaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
||||||
},
|
},
|
||||||
filterDescriptor =>
|
filterDescriptor =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(applicationModel.Filters,
|
Assert.Collection(applicationModel.Filters,
|
||||||
filter => Assert.IsType<SaveTempDataPropertyFilterFactory>(filter),
|
filter => Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filter),
|
||||||
filter => Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filter));
|
filter => Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
{
|
||||||
|
public class ControllerSaveTempDataPropertyFilterFactoryTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CreateInstance_CreatesFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var factory = new ControllerSaveTempDataPropertyFilterFactory();
|
||||||
|
var propertyInfo = typeof(StringController).GetProperty("StringProp");
|
||||||
|
|
||||||
|
factory.TempDataProperties = new List<TempDataProperty>()
|
||||||
|
{
|
||||||
|
new TempDataProperty(propertyInfo, null, null)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var filter = factory.CreateInstance(CreateServiceProvider());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var controllerFilter = Assert.IsType<ControllerSaveTempDataPropertyFilter>(filter);
|
||||||
|
Assert.Collection(controllerFilter.TempDataProperties,
|
||||||
|
property => Assert.Equal("StringProp", property.PropertyInfo.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceProvider CreateServiceProvider()
|
||||||
|
{
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
|
||||||
|
serviceCollection.AddSingleton(Mock.Of<ITempDataProvider>());
|
||||||
|
serviceCollection.AddSingleton<ITempDataDictionaryFactory, TempDataDictionaryFactory>();
|
||||||
|
serviceCollection.AddTransient<ControllerSaveTempDataPropertyFilter>();
|
||||||
|
|
||||||
|
return serviceCollection.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StringController
|
||||||
|
{
|
||||||
|
public string StringProp { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,21 +2,19 @@
|
||||||
// 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 System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
{
|
{
|
||||||
public class SaveTempDataPropertyFilterTest
|
public class ControllerSaveTempDataPropertyFilterTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SaveTempDataPropertyFilter_PopulatesTempDataWithValuesFromControllerProperty()
|
public void PopulatesTempDataWithValuesFromControllerProperty()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -25,11 +23,20 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
["TempDataProperty-Test"] = "FirstValue"
|
["TempDataProperty-Test"] = "FirstValue"
|
||||||
};
|
};
|
||||||
|
|
||||||
var filter = CreateSaveTempDataPropertyFilter(httpContext, tempData);
|
var filter = CreateControllerSaveTempDataPropertyFilter(httpContext, tempData);
|
||||||
|
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
|
|
||||||
filter.PropertyHelpers = BuildPropertyHelpers<TestController>();
|
var controllerType = controller.GetType();
|
||||||
|
var testProp = controllerType.GetProperty(nameof(TestController.Test));
|
||||||
|
var test2Prop = controllerType.GetProperty(nameof(TestController.Test2));
|
||||||
|
|
||||||
|
filter.TempDataProperties = new List<TempDataProperty>
|
||||||
|
{
|
||||||
|
new TempDataProperty(testProp, testProp.GetValue, testProp.SetValue),
|
||||||
|
new TempDataProperty(test2Prop, test2Prop.GetValue, test2Prop.SetValue)
|
||||||
|
};
|
||||||
|
|
||||||
var context = new ActionExecutingContext(
|
var context = new ActionExecutingContext(
|
||||||
new ActionContext
|
new ActionContext
|
||||||
{
|
{
|
||||||
|
|
@ -53,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SaveTempDataPropertyFilter_ReadsTempDataFromTempDataDictionary()
|
public void ReadsTempDataFromTempDataDictionary()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -62,10 +69,18 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
["TempDataProperty-Test"] = "FirstValue"
|
["TempDataProperty-Test"] = "FirstValue"
|
||||||
};
|
};
|
||||||
|
|
||||||
var filter = CreateSaveTempDataPropertyFilter(httpContext, tempData: tempData);
|
var filter = CreateControllerSaveTempDataPropertyFilter(httpContext, tempData: tempData);
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
|
|
||||||
filter.PropertyHelpers = BuildPropertyHelpers<TestController>();
|
var controllerType = controller.GetType();
|
||||||
|
var testProp = controllerType.GetProperty(nameof(TestController.Test));
|
||||||
|
var test2Prop = controllerType.GetProperty(nameof(TestController.Test2));
|
||||||
|
|
||||||
|
filter.TempDataProperties = new List<TempDataProperty>
|
||||||
|
{
|
||||||
|
new TempDataProperty(testProp, testProp.GetValue, testProp.SetValue),
|
||||||
|
new TempDataProperty(test2Prop, test2Prop.GetValue, test2Prop.SetValue)
|
||||||
|
};
|
||||||
|
|
||||||
var context = new ActionExecutingContext(
|
var context = new ActionExecutingContext(
|
||||||
new ActionContext
|
new ActionContext
|
||||||
|
|
@ -87,53 +102,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
Assert.Equal(0, controller.Test2);
|
Assert.Equal(0, controller.Test2);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
private ControllerSaveTempDataPropertyFilter CreateControllerSaveTempDataPropertyFilter(
|
||||||
public void ApplyTempDataChanges_SetsPropertyValue()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
|
|
||||||
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>())
|
|
||||||
{
|
|
||||||
{ "TempDataProperty-Test", "Value" }
|
|
||||||
};
|
|
||||||
tempData.Save();
|
|
||||||
|
|
||||||
var controller = new TestControllerStrings()
|
|
||||||
{
|
|
||||||
TempData = tempData,
|
|
||||||
};
|
|
||||||
|
|
||||||
var provider = CreateSaveTempDataPropertyFilter(httpContext, tempData: tempData);
|
|
||||||
provider.Subject = controller;
|
|
||||||
provider.PropertyHelpers = BuildPropertyHelpers<TestControllerStrings>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
provider.ApplyTempDataChanges(httpContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("Value", controller.Test);
|
|
||||||
Assert.Null(controller.Test2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IList<PropertyHelper> BuildPropertyHelpers<TSubject>()
|
|
||||||
{
|
|
||||||
var subjectType = typeof(TSubject);
|
|
||||||
|
|
||||||
var properties = subjectType.GetProperties(
|
|
||||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
|
||||||
|
|
||||||
var result = new List<PropertyHelper>();
|
|
||||||
|
|
||||||
foreach (var property in properties)
|
|
||||||
{
|
|
||||||
result.Add(new PropertyHelper(property));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SaveTempDataPropertyFilter CreateSaveTempDataPropertyFilter(
|
|
||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
TempDataDictionary tempData)
|
TempDataDictionary tempData)
|
||||||
{
|
{
|
||||||
|
|
@ -141,16 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
factory.Setup(f => f.GetTempData(httpContext))
|
factory.Setup(f => f.GetTempData(httpContext))
|
||||||
.Returns(tempData);
|
.Returns(tempData);
|
||||||
|
|
||||||
return new SaveTempDataPropertyFilter(factory.Object);
|
return new ControllerSaveTempDataPropertyFilter(factory.Object);
|
||||||
}
|
|
||||||
|
|
||||||
public class TestControllerStrings : Controller
|
|
||||||
{
|
|
||||||
[TempData]
|
|
||||||
public string Test { get; set; }
|
|
||||||
|
|
||||||
[TempData]
|
|
||||||
public string Test2 { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestController : Controller
|
public class TestController : Controller
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 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 Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
{
|
||||||
|
public class PageSaveTempDataPropertyFilterFactoryTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CreatesInstanceWithProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var factory = new PageSaveTempDataPropertyFilterFactory();
|
||||||
|
|
||||||
|
var serviceProvider = CreateServiceProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var filter = factory.CreateInstance(serviceProvider);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var pageFilter = Assert.IsType<PageSaveTempDataPropertyFilter>(filter);
|
||||||
|
Assert.Same(factory, pageFilter.FilterFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceProvider CreateServiceProvider()
|
||||||
|
{
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
|
||||||
|
serviceCollection.AddSingleton(Mock.Of<ITempDataProvider>());
|
||||||
|
serviceCollection.AddSingleton<ITempDataDictionaryFactory, TempDataDictionaryFactory>();
|
||||||
|
serviceCollection.AddTransient<PageSaveTempDataPropertyFilter>();
|
||||||
|
|
||||||
|
return serviceCollection.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,246 @@
|
||||||
|
// 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 System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
{
|
||||||
|
public class PageSaveTempDataPropertyFilterTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void OnTempDataSaving_PopulatesTempDataWithValuesFromPageProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
||||||
|
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>())
|
||||||
|
{
|
||||||
|
{ "TempDataProperty-Test", "TestString" }
|
||||||
|
};
|
||||||
|
tempData.Save();
|
||||||
|
|
||||||
|
var page = new TestPageString()
|
||||||
|
{
|
||||||
|
Test = "TestString",
|
||||||
|
ViewContext = CreateViewContext(httpContext, tempData)
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = CreatePageSaveTempDataPropertyFilter(httpContext, tempData: tempData);
|
||||||
|
provider.Subject = page;
|
||||||
|
|
||||||
|
var pageType = page.GetType();
|
||||||
|
|
||||||
|
var testProp = pageType.GetProperty(nameof(TestPageString.Test));
|
||||||
|
var test2Prop = pageType.GetProperty(nameof(TestPageString.Test2));
|
||||||
|
|
||||||
|
provider.TempDataProperties = new List<TempDataProperty>{
|
||||||
|
new TempDataProperty(testProp, testProp.GetValue, testProp.SetValue),
|
||||||
|
new TempDataProperty(test2Prop, test2Prop.GetValue, test2Prop.SetValue)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.OnTempDataSaving(tempData);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("TestString", page.Test);
|
||||||
|
Assert.Equal("TestString", page.TempData["TempDataProperty-Test"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetSubject_NullFilterFactory_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
|
||||||
|
tempData.Save();
|
||||||
|
|
||||||
|
var page = new TestPageString()
|
||||||
|
{
|
||||||
|
ViewContext = CreateViewContext(httpContext, tempData)
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = CreatePageSaveTempDataPropertyFilter(httpContext, tempData: tempData, filterFactory: false);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = Assert.Throws<InvalidOperationException>(() => provider.Subject = page);
|
||||||
|
Assert.Contains("FilterFactory", ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetSubject_ModifiesFactoryAndFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
||||||
|
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
|
||||||
|
tempData.Save();
|
||||||
|
|
||||||
|
var page = new TestPageString()
|
||||||
|
{
|
||||||
|
Test = "TestString",
|
||||||
|
ViewContext = CreateViewContext(httpContext, tempData)
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = CreatePageSaveTempDataPropertyFilter(httpContext, tempData: tempData);
|
||||||
|
provider.FilterFactory = new PageSaveTempDataPropertyFilterFactory();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.Subject = page;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(provider.TempDataProperties,
|
||||||
|
property => Assert.Equal("Test", property.PropertyInfo.Name),
|
||||||
|
property => Assert.Equal("Test2", property.PropertyInfo.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ApplyTempDataChanges_ToPageModel_SetsPropertyValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
||||||
|
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>())
|
||||||
|
{
|
||||||
|
{ "TempDataProperty-Test", "Value" }
|
||||||
|
};
|
||||||
|
tempData.Save();
|
||||||
|
|
||||||
|
var page = new TestPageString()
|
||||||
|
{
|
||||||
|
ViewContext = CreateViewContext(httpContext, tempData)
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = CreatePageSaveTempDataPropertyFilter(httpContext, tempData: tempData);
|
||||||
|
provider.Subject = page;
|
||||||
|
|
||||||
|
var pageType = typeof(TestPageString);
|
||||||
|
var testProp = pageType.GetProperty("Test");
|
||||||
|
var test2Prop = pageType.GetProperty("Test2");
|
||||||
|
|
||||||
|
provider.TempDataProperties = new List<TempDataProperty> {
|
||||||
|
new TempDataProperty(testProp, testProp.GetValue, testProp.SetValue),
|
||||||
|
new TempDataProperty(test2Prop, test2Prop.GetValue, test2Prop.SetValue)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.ApplyTempDataChanges(httpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Value", page.Test);
|
||||||
|
Assert.Null(page.Test2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ApplyTempDataChanges_ToPage_SetsPropertyValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
||||||
|
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>())
|
||||||
|
{
|
||||||
|
{ "TempDataProperty-Test", "Value" }
|
||||||
|
};
|
||||||
|
tempData.Save();
|
||||||
|
|
||||||
|
var page = new TestPageString()
|
||||||
|
{
|
||||||
|
ViewContext = CreateViewContext(httpContext, tempData)
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = CreatePageSaveTempDataPropertyFilter(httpContext, tempData: tempData);
|
||||||
|
provider.Subject = page;
|
||||||
|
|
||||||
|
var pageType = page.GetType();
|
||||||
|
|
||||||
|
var testProp = pageType.GetProperty(nameof(TestPageString.Test));
|
||||||
|
var test2Prop = pageType.GetProperty(nameof(TestPageString.Test2));
|
||||||
|
|
||||||
|
provider.TempDataProperties = new List<TempDataProperty> {
|
||||||
|
new TempDataProperty(testProp, testProp.GetValue, testProp.SetValue),
|
||||||
|
new TempDataProperty(test2Prop, test2Prop.GetValue, test2Prop.SetValue)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.ApplyTempDataChanges(httpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Value", page.Test);
|
||||||
|
Assert.Null(page.Test2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PageContext CreateViewContext(HttpContext httpContext, ITempDataDictionary tempData)
|
||||||
|
{
|
||||||
|
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var viewData = new ViewDataDictionary(metadataProvider, new ModelStateDictionary());
|
||||||
|
var viewContext = new PageContext(
|
||||||
|
actionContext,
|
||||||
|
viewData,
|
||||||
|
tempData,
|
||||||
|
new HtmlHelperOptions());
|
||||||
|
|
||||||
|
return viewContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PageSaveTempDataPropertyFilter CreatePageSaveTempDataPropertyFilter(
|
||||||
|
HttpContext httpContext,
|
||||||
|
TempDataDictionary tempData,
|
||||||
|
bool filterFactory = true)
|
||||||
|
{
|
||||||
|
var factory = new Mock<ITempDataDictionaryFactory>();
|
||||||
|
factory.Setup(f => f.GetTempData(httpContext))
|
||||||
|
.Returns(tempData);
|
||||||
|
|
||||||
|
var propertyFilter = new PageSaveTempDataPropertyFilter(factory.Object);
|
||||||
|
|
||||||
|
if (filterFactory)
|
||||||
|
{
|
||||||
|
propertyFilter.FilterFactory = new Mock<PageSaveTempDataPropertyFilterFactory>().Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertyFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestPageString : Page
|
||||||
|
{
|
||||||
|
[TempData]
|
||||||
|
public string Test { get; set; }
|
||||||
|
|
||||||
|
[TempData]
|
||||||
|
public string Test2 { get; set; }
|
||||||
|
|
||||||
|
public override Task ExecuteAsync()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestPageStringWithModel : Page
|
||||||
|
{
|
||||||
|
public PageModel TestPageModelWithString { get; set; }
|
||||||
|
|
||||||
|
public override Task ExecuteAsync()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestPageModelWithString : PageModel
|
||||||
|
{
|
||||||
|
[TempData]
|
||||||
|
public string Test { get; set; }
|
||||||
|
|
||||||
|
[TempData]
|
||||||
|
public string Test2 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var controller = Assert.Single(context.Result.Controllers);
|
var controller = Assert.Single(context.Result.Controllers);
|
||||||
Assert.Single(controller.Filters, f => f is SaveTempDataPropertyFilterFactory);
|
Assert.Single(controller.Filters, f => f is ControllerSaveTempDataPropertyFilterFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -46,14 +46,14 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
// Act
|
// Act
|
||||||
provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
var controller = context.Result.Controllers.SingleOrDefault();
|
var controller = context.Result.Controllers.SingleOrDefault();
|
||||||
var filter = controller.Filters.OfType<SaveTempDataPropertyFilterFactory>();
|
var filter = controller.Filters.OfType<ControllerSaveTempDataPropertyFilterFactory>();
|
||||||
var saveTempDataPropertyFilterFactory = filter.SingleOrDefault();
|
var saveTempDataPropertyFilterFactory = filter.SingleOrDefault();
|
||||||
var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2));
|
var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2));
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(saveTempDataPropertyFilterFactory);
|
Assert.NotNull(saveTempDataPropertyFilterFactory);
|
||||||
var tempDataPropertyHelper = Assert.Single(saveTempDataPropertyFilterFactory.TempDataProperties);
|
var tempDataPropertyHelper = Assert.Single(saveTempDataPropertyFilterFactory.TempDataProperties);
|
||||||
Assert.Same(expected, tempDataPropertyHelper.Property);
|
Assert.Same(expected, tempDataPropertyHelper.PropertyInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
@page
|
||||||
|
|
||||||
|
@functions
|
||||||
|
{
|
||||||
|
public IActionResult OnGet()
|
||||||
|
{
|
||||||
|
TempData["TempDataProperty-Message"] = "Secret Message";
|
||||||
|
return Redirect("~/TempData/TempDataPageModelProperty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
|
||||||
|
namespace RazorPagesWebSite.TempData
|
||||||
|
{
|
||||||
|
public class TempDataPageModel : PageModel
|
||||||
|
{
|
||||||
|
[TempData]
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public IActionResult OnGet()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult OnPost()
|
||||||
|
{
|
||||||
|
Message = "Secret post";
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
@page
|
||||||
|
@model RazorPagesWebSite.TempData.TempDataPageModel
|
||||||
|
|
||||||
|
Message: @Model.Message
|
||||||
|
@using (Html.BeginForm())
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
}
|
||||||
|
TempData: @TempData["TempDataProperty-Message"]
|
||||||
Loading…
Reference in New Issue