parent
7166dfecd7
commit
8df3032540
|
|
@ -0,0 +1,17 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows customization of the of the <see cref="PageRouteModel"/>.
|
||||
/// </summary>
|
||||
public interface IPageRouteModelConvention
|
||||
{
|
||||
/// <summary>
|
||||
/// Called to apply the convention to the <see cref="PageRouteModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="model">The <see cref="PageRouteModel"/>.</param>
|
||||
void Apply(PageRouteModel model);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds or modifies an <see cref="PageRouteModelProviderContext"/> for Razor Page routing.
|
||||
/// </summary>
|
||||
public interface IPageRouteModelProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the order value for determining the order of execution of providers. Providers execute in
|
||||
/// ascending numeric value of the <see cref="Order"/> property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Providers are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
|
||||
/// A provider with a lower numeric value of <see cref="Order"/> will have its
|
||||
/// <see cref="OnProvidersExecuting"/> called before that of a provider with a higher numeric value of
|
||||
/// <see cref="Order"/>. The <see cref="OnProvidersExecuted"/> method is called in the reverse ordering after
|
||||
/// all calls to <see cref="OnProvidersExecuting"/>. A provider with a lower numeric value of
|
||||
/// <see cref="Order"/> will have its <see cref="OnProvidersExecuted"/> method called after that of a provider
|
||||
/// with a higher numeric value of <see cref="Order"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If two providers have the same numeric value of <see cref="Order"/>, then their relative execution order
|
||||
/// is undefined.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
int Order { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Executed for the first pass of building <see cref="PageRouteModel"/> instances. See <see cref="Order"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageRouteModelProviderContext"/>.</param>
|
||||
void OnProvidersExecuting(PageRouteModelProviderContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Executed for the second pass of building <see cref="PageRouteModel"/> instances. See <see cref="Order"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageRouteModelProviderContext"/>.</param>
|
||||
void OnProvidersExecuted(PageRouteModelProviderContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
|
|
@ -16,26 +19,21 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="PageApplicationModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The application relative path of the page.</param>
|
||||
/// <param name="viewEnginePath">The path relative to the base path for page discovery.</param>
|
||||
public PageApplicationModel(string relativePath, string viewEnginePath)
|
||||
public PageApplicationModel(
|
||||
PageActionDescriptor actionDescriptor,
|
||||
TypeInfo handlerType,
|
||||
IReadOnlyList<object> handlerAttributes)
|
||||
{
|
||||
if (relativePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(relativePath));
|
||||
}
|
||||
|
||||
if (viewEnginePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(viewEnginePath));
|
||||
}
|
||||
|
||||
RelativePath = relativePath;
|
||||
ViewEnginePath = viewEnginePath;
|
||||
ActionDescriptor = actionDescriptor ?? throw new ArgumentNullException(nameof(actionDescriptor));
|
||||
HandlerType = handlerType;
|
||||
|
||||
Filters = new List<IFilterMetadata>();
|
||||
Properties = new Dictionary<object, object>();
|
||||
Selectors = new List<SelectorModel>();
|
||||
Properties = new CopyOnWriteDictionary<object, object>(
|
||||
actionDescriptor.Properties,
|
||||
EqualityComparer<object>.Default);
|
||||
HandlerMethods = new List<PageHandlerModel>();
|
||||
HandlerProperties = new List<PagePropertyModel>();
|
||||
HandlerTypeAttributes = handlerAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -49,24 +47,38 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
RelativePath = other.RelativePath;
|
||||
ViewEnginePath = other.ViewEnginePath;
|
||||
ActionDescriptor = other.ActionDescriptor;
|
||||
HandlerType = other.HandlerType;
|
||||
PageType = other.PageType;
|
||||
ModelType = other.ModelType;
|
||||
|
||||
Filters = new List<IFilterMetadata>(other.Filters);
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
|
||||
Selectors = new List<SelectorModel>(other.Selectors.Select(m => new SelectorModel(m)));
|
||||
HandlerMethods = new List<PageHandlerModel>(other.HandlerMethods.Select(m => new PageHandlerModel(m)));
|
||||
HandlerProperties = new List<PagePropertyModel>(other.HandlerProperties.Select(p => new PagePropertyModel(p)));
|
||||
HandlerTypeAttributes = other.HandlerTypeAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="PageActionDescriptor"/>.
|
||||
/// </summary>
|
||||
public PageActionDescriptor ActionDescriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application root relative path for the page.
|
||||
/// </summary>
|
||||
public string RelativePath { get; }
|
||||
public string RelativePath => ActionDescriptor.RelativePath;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path relative to the base path for page discovery.
|
||||
/// </summary>
|
||||
public string ViewEnginePath { get; }
|
||||
public string ViewEnginePath => ActionDescriptor.ViewEnginePath;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route template for the page.
|
||||
/// </summary>
|
||||
public string RouteTemplate => ActionDescriptor.AttributeRouteInfo?.Template;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the applicable <see cref="IFilterMetadata"/> instances.
|
||||
|
|
@ -79,8 +91,33 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="SelectorModel"/> instances.
|
||||
/// Gets or sets the <see cref="TypeInfo"/> of the Razor page.
|
||||
/// </summary>
|
||||
public IList<SelectorModel> Selectors { get; }
|
||||
public TypeInfo PageType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TypeInfo"/> of the Razor page model.
|
||||
/// </summary>
|
||||
public TypeInfo ModelType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="TypeInfo"/> of the handler.
|
||||
/// </summary>
|
||||
public TypeInfo HandlerType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence of attributes declared on <see cref="HandlerType"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<object> HandlerTypeAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence of <see cref="PageHandlerModel"/> instances.
|
||||
/// </summary>
|
||||
public IList<PageHandlerModel> HandlerMethods { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence of <see cref="PagePropertyModel"/> instances on <see cref="PageHandlerModel"/>.
|
||||
/// </summary>
|
||||
public IList<PagePropertyModel> HandlerProperties { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
// 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 System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
|
|
@ -10,9 +11,25 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// </summary>
|
||||
public class PageApplicationModelProviderContext
|
||||
{
|
||||
public PageApplicationModelProviderContext(PageActionDescriptor descriptor, TypeInfo pageTypeInfo)
|
||||
{
|
||||
ActionDescriptor = descriptor;
|
||||
PageType = pageTypeInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="PageApplicationModel"/> instances.
|
||||
/// Gets the <see cref="PageActionDescriptor"/>.
|
||||
/// </summary>
|
||||
public IList<PageApplicationModel> Results { get; } = new List<PageApplicationModel>();
|
||||
public PageActionDescriptor ActionDescriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the page <see cref="TypeInfo"/>.
|
||||
/// </summary>
|
||||
public TypeInfo PageType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ApplicationModels.PageApplicationModel"/>.
|
||||
/// </summary>
|
||||
public PageApplicationModel PageApplicationModel { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a handler in a <see cref="PageApplicationModel"/>.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("PageHandlerModel: Name={" + nameof(PageHandlerModel.Name) + "}")]
|
||||
public class PageHandlerModel : ICommonModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="PageHandlerModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="handlerMethod">The <see cref="System.Reflection.MethodInfo"/> for the handler.</param>
|
||||
/// <param name="attributes">Any attributes annotated on the handler method.</param>
|
||||
public PageHandlerModel(
|
||||
MethodInfo handlerMethod,
|
||||
IReadOnlyList<object> attributes)
|
||||
{
|
||||
MethodInfo = handlerMethod ?? throw new ArgumentNullException(nameof(handlerMethod));
|
||||
Attributes = attributes ?? throw new ArgumentNullException(nameof(attributes));
|
||||
|
||||
Parameters = new List<PageParameterModel>();
|
||||
Properties = new Dictionary<object, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creats a new instance of <see cref="PageHandlerModel"/> from a given <see cref="PageHandlerModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">The <see cref="PageHandlerModel"/> which needs to be copied.</param>
|
||||
public PageHandlerModel(PageHandlerModel other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
MethodInfo = other.MethodInfo;
|
||||
HandlerName = other.HandlerName;
|
||||
HttpMethod = other.HttpMethod;
|
||||
Name = other.Name;
|
||||
|
||||
Page = other.Page;
|
||||
|
||||
// These are just metadata, safe to create new collections
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
|
||||
// Make a deep copy of other 'model' types.
|
||||
Parameters = new List<PageParameterModel>(other.Parameters.Select(p => new PageParameterModel(p) { Handler = this }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="System.Reflection.MethodInfo"/> for the handler.
|
||||
/// </summary>
|
||||
public MethodInfo MethodInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP method supported by this handler.
|
||||
/// </summary>
|
||||
public string HttpMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler method name.
|
||||
/// </summary>
|
||||
public string HandlerName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a descriptive name for the handler.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence of <see cref="PageParameterModel"/> instances.
|
||||
/// </summary>
|
||||
public IList<PageParameterModel> Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="PageApplicationModel"/>.
|
||||
/// </summary>
|
||||
public PageApplicationModel Page { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
MemberInfo ICommonModel.MemberInfo => MethodInfo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
[DebuggerDisplay("PageParameterModel: Name={ParameterName}")]
|
||||
public class PageParameterModel : ICommonModel, IBindingModel
|
||||
{
|
||||
public PageParameterModel(
|
||||
ParameterInfo parameterInfo,
|
||||
IReadOnlyList<object> attributes)
|
||||
{
|
||||
ParameterInfo = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo));
|
||||
|
||||
if (attributes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
Properties = new Dictionary<object, object>();
|
||||
Attributes = new List<object>(attributes);
|
||||
}
|
||||
|
||||
public PageParameterModel(PageParameterModel other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
Handler = other.Handler;
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
BindingInfo = other.BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
|
||||
ParameterInfo = other.ParameterInfo;
|
||||
ParameterName = other.ParameterName;
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
}
|
||||
|
||||
public PageHandlerModel Handler { get; set; }
|
||||
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
MemberInfo ICommonModel.MemberInfo => ParameterInfo.Member;
|
||||
|
||||
string ICommonModel.Name => ParameterName;
|
||||
|
||||
public ParameterInfo ParameterInfo { get; }
|
||||
|
||||
public string ParameterName { get; set; }
|
||||
|
||||
public BindingInfo BindingInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a property in a <see cref="PageApplicationModel"/>.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("PagePropertyModel: Name={PropertyName}")]
|
||||
public class PagePropertyModel : ICommonModel, IBindingModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PagePropertyModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">The <see cref="PropertyInfo"/> for the underlying property.</param>
|
||||
/// <param name="attributes">Any attributes which are annotated on the property.</param>
|
||||
public PagePropertyModel(
|
||||
PropertyInfo propertyInfo,
|
||||
IReadOnlyList<object> attributes)
|
||||
{
|
||||
PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo));
|
||||
Properties = new Dictionary<object, object>();
|
||||
Attributes = new List<object>(attributes) ?? throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creats a new instance of <see cref="PagePropertyModel"/> from a given <see cref="PagePropertyModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">The <see cref="PagePropertyModel"/> which needs to be copied.</param>
|
||||
public PagePropertyModel(PagePropertyModel other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
Page = other.Page;
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
BindingInfo = BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
|
||||
PropertyInfo = other.PropertyInfo;
|
||||
PropertyName = other.PropertyName;
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="PageApplicationModel"/> this <see cref="PagePropertyModel"/> is associated with.
|
||||
/// </summary>
|
||||
public PageApplicationModel Page { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets any attributes which are annotated on the property.
|
||||
/// </summary>
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="BindingInfo"/> associated with this model.
|
||||
/// </summary>
|
||||
public BindingInfo BindingInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying <see cref="PropertyInfo"/>.
|
||||
/// </summary>
|
||||
public PropertyInfo PropertyInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property represented by this model.
|
||||
/// </summary>
|
||||
public string PropertyName { get; set; }
|
||||
|
||||
MemberInfo ICommonModel.MemberInfo => PropertyInfo;
|
||||
|
||||
string ICommonModel.Name => PropertyName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// 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.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// A model component for routing RazorPages.
|
||||
/// </summary>
|
||||
public class PageRouteModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="PageRouteModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The application relative path of the page.</param>
|
||||
/// <param name="viewEnginePath">The path relative to the base path for page discovery.</param>
|
||||
public PageRouteModel(string relativePath, string viewEnginePath)
|
||||
{
|
||||
if (relativePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(relativePath));
|
||||
}
|
||||
|
||||
if (viewEnginePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(viewEnginePath));
|
||||
}
|
||||
|
||||
RelativePath = relativePath;
|
||||
ViewEnginePath = viewEnginePath;
|
||||
|
||||
Properties = new Dictionary<object, object>();
|
||||
Selectors = new List<SelectorModel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A copy constructor for <see cref="PageRouteModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">The <see cref="PageRouteModel"/> to copy from.</param>
|
||||
public PageRouteModel(PageRouteModel other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
RelativePath = other.RelativePath;
|
||||
ViewEnginePath = other.ViewEnginePath;
|
||||
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
Selectors = new List<SelectorModel>(other.Selectors.Select(m => new SelectorModel(m)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application root relative path for the page.
|
||||
/// </summary>
|
||||
public string RelativePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path relative to the base path for page discovery.
|
||||
/// </summary>
|
||||
public string ViewEnginePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores arbitrary metadata properties associated with the <see cref="PageRouteModel"/>.
|
||||
/// </summary>
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="SelectorModel"/> instances.
|
||||
/// </summary>
|
||||
public IList<SelectorModel> Selectors { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// A context object for <see cref="IPageRouteModelProvider"/>.
|
||||
/// </summary>
|
||||
public class PageRouteModelProviderContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="PageRouteModel"/> instances.
|
||||
/// </summary>
|
||||
public IList<PageRouteModel> RouteModels { get; } = new List<PageRouteModel>();
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Mvc.Razor;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
|
|
@ -88,11 +87,18 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
ServiceDescriptor.Singleton<IActionDescriptorProvider, PageActionDescriptorProvider>());
|
||||
services.TryAddSingleton<IActionDescriptorChangeProvider, PageActionDescriptorChangeProvider>();
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, RazorProjectPageApplicationModelProvider>());
|
||||
ServiceDescriptor.Singleton<IPageRouteModelProvider, RazorProjectPageRouteModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, CompiledPageApplicationModelProvider>());
|
||||
ServiceDescriptor.Singleton<IPageRouteModelProvider, CompiledPageRouteModelProvider>());
|
||||
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, PageFilterApplicationModelProvider>());
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, DefaultPageApplicationModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, AutoValidateAntiforgeryPageApplicationModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, AuthorizationPageApplicationModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, TempDataFilterPageApplicationModelProvider>());
|
||||
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IActionInvokerProvider, PageActionInvokerProvider>());
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
options.Conventions.Add(new FolderConvention("/", model => model.Filters.Add(factory(model))));
|
||||
options.ApplicationModelConventions.Add(new FolderApplicationModelConvention("/", model => model.Filters.Add(factory(model))));
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
throw new ArgumentNullException(nameof(filter));
|
||||
}
|
||||
|
||||
options.Conventions.Add(new FolderConvention("/", model => model.Filters.Add(filter)));
|
||||
options.ApplicationModelConventions.Add(new FolderApplicationModelConvention("/", model => model.Filters.Add(filter)));
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
var anonymousFilter = new AllowAnonymousFilter();
|
||||
options.Conventions.Add(new PageConvention(pageName, model => model.Filters.Add(anonymousFilter)));
|
||||
options.ApplicationModelConventions.Add(new PageApplicationModelConvention(pageName, model => model.Filters.Add(anonymousFilter)));
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
var anonymousFilter = new AllowAnonymousFilter();
|
||||
options.Conventions.Add(new FolderConvention(folderPath, model => model.Filters.Add(anonymousFilter)));
|
||||
options.ApplicationModelConventions.Add(new FolderApplicationModelConvention(folderPath, model => model.Filters.Add(anonymousFilter)));
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
var authorizeFilter = new AuthorizeFilter(policy);
|
||||
options.Conventions.Add(new PageConvention(pageName, model => model.Filters.Add(authorizeFilter)));
|
||||
options.ApplicationModelConventions.Add(new PageApplicationModelConvention(pageName, model => model.Filters.Add(authorizeFilter)));
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
var authorizeFilter = new AuthorizeFilter(policy);
|
||||
options.Conventions.Add(new FolderConvention(folderPath, model => model.Filters.Add(authorizeFilter)));
|
||||
options.ApplicationModelConventions.Add(new FolderApplicationModelConvention(folderPath, model => model.Filters.Add(authorizeFilter)));
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
throw new ArgumentNullException(nameof(route));
|
||||
}
|
||||
|
||||
options.Conventions.Add(new PageConvention(pageName, model =>
|
||||
options.RouteModelConventions.Add(new PageRouteModelConvention(pageName, model =>
|
||||
{
|
||||
// Use the route specified in MapPageRoute for outbound routing.
|
||||
foreach (var selector in model.Selectors)
|
||||
|
|
@ -218,12 +218,54 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
return options;
|
||||
}
|
||||
|
||||
private class PageConvention : IPageApplicationModelConvention
|
||||
private class PageRouteModelConvention : IPageRouteModelConvention
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly Action<PageRouteModel> _action;
|
||||
|
||||
public PageRouteModelConvention(string path, Action<PageRouteModel> action)
|
||||
{
|
||||
_path = path;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public void Apply(PageRouteModel model)
|
||||
{
|
||||
if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_action(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FolderRouteModelConvention : IPageRouteModelConvention
|
||||
{
|
||||
private readonly string _folderPath;
|
||||
private readonly Action<PageRouteModel> _action;
|
||||
|
||||
public FolderRouteModelConvention(string folderPath, Action<PageRouteModel> action)
|
||||
{
|
||||
_folderPath = folderPath.TrimEnd('/');
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public void Apply(PageRouteModel model)
|
||||
{
|
||||
var viewEnginePath = model.ViewEnginePath;
|
||||
|
||||
if (PathBelongsToFolder(_folderPath, viewEnginePath))
|
||||
{
|
||||
_action(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PageApplicationModelConvention : IPageApplicationModelConvention
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly Action<PageApplicationModel> _action;
|
||||
|
||||
public PageConvention(string path, Action<PageApplicationModel> action)
|
||||
public PageApplicationModelConvention(string path, Action<PageApplicationModel> action)
|
||||
{
|
||||
_path = path;
|
||||
_action = action;
|
||||
|
|
@ -238,12 +280,12 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
}
|
||||
|
||||
private class FolderConvention : IPageApplicationModelConvention
|
||||
private class FolderApplicationModelConvention : IPageApplicationModelConvention
|
||||
{
|
||||
private readonly string _folderPath;
|
||||
private readonly Action<PageApplicationModel> _action;
|
||||
|
||||
public FolderConvention(string folderPath, Action<PageApplicationModel> action)
|
||||
public FolderApplicationModelConvention(string folderPath, Action<PageApplicationModel> action)
|
||||
{
|
||||
_folderPath = folderPath.TrimEnd('/');
|
||||
_action = action;
|
||||
|
|
@ -253,16 +295,24 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
var viewEnginePath = model.ViewEnginePath;
|
||||
|
||||
var applyConvention = _folderPath == "/" ||
|
||||
(viewEnginePath.Length > _folderPath.Length &&
|
||||
viewEnginePath.StartsWith(_folderPath, StringComparison.OrdinalIgnoreCase) &&
|
||||
viewEnginePath[_folderPath.Length] == '/');
|
||||
|
||||
if (applyConvention)
|
||||
if (PathBelongsToFolder(_folderPath, viewEnginePath))
|
||||
{
|
||||
_action(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PathBelongsToFolder(string folderPath, string viewEnginePath)
|
||||
{
|
||||
if (folderPath == "/")
|
||||
{
|
||||
// Root directory covers everything.
|
||||
return true;
|
||||
}
|
||||
|
||||
return viewEnginePath.Length > folderPath.Length &&
|
||||
viewEnginePath.StartsWith(folderPath, StringComparison.OrdinalIgnoreCase) &&
|
||||
viewEnginePath[folderPath.Length] == '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
public class PageActionDescriptorProvider : IActionDescriptorProvider
|
||||
{
|
||||
private readonly List<IPageApplicationModelProvider> _applicationModelProviders;
|
||||
private readonly IPageRouteModelProvider[] _routeModelProviders;
|
||||
private readonly MvcOptions _mvcOptions;
|
||||
private readonly RazorPagesOptions _pagesOptions;
|
||||
|
||||
public PageActionDescriptorProvider(
|
||||
IEnumerable<IPageApplicationModelProvider> pageMetadataProviders,
|
||||
IEnumerable<IPageRouteModelProvider> pageRouteModelProviders,
|
||||
IOptions<MvcOptions> mvcOptionsAccessor,
|
||||
IOptions<RazorPagesOptions> pagesOptionsAccessor)
|
||||
{
|
||||
_applicationModelProviders = pageMetadataProviders.OrderBy(p => p.Order).ToList();
|
||||
_routeModelProviders = pageRouteModelProviders.OrderBy(p => p.Order).ToArray();
|
||||
_mvcOptions = mvcOptionsAccessor.Value;
|
||||
_pagesOptions = pagesOptionsAccessor.Value;
|
||||
}
|
||||
|
|
@ -32,51 +32,40 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
|
||||
public void OnProvidersExecuting(ActionDescriptorProviderContext context)
|
||||
{
|
||||
var pageApplicationModels = BuildModel();
|
||||
var pageRouteModels = BuildModel();
|
||||
|
||||
for (var i = 0; i < pageApplicationModels.Count; i++)
|
||||
for (var i = 0; i < pageRouteModels.Count; i++)
|
||||
{
|
||||
AddActionDescriptors(context.Results, pageApplicationModels[i]);
|
||||
AddActionDescriptors(context.Results, pageRouteModels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
protected IList<PageApplicationModel> BuildModel()
|
||||
protected IList<PageRouteModel> BuildModel()
|
||||
{
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
for (var i = 0; i < _applicationModelProviders.Count; i++)
|
||||
for (var i = 0; i < _routeModelProviders.Length; i++)
|
||||
{
|
||||
_applicationModelProviders[i].OnProvidersExecuting(context);
|
||||
_routeModelProviders[i].OnProvidersExecuting(context);
|
||||
}
|
||||
|
||||
for (var i = _applicationModelProviders.Count - 1; i >= 0; i--)
|
||||
for (var i = _routeModelProviders.Length - 1; i >= 0; i--)
|
||||
{
|
||||
_applicationModelProviders[i].OnProvidersExecuted(context);
|
||||
_routeModelProviders[i].OnProvidersExecuted(context);
|
||||
}
|
||||
|
||||
return context.Results;
|
||||
return context.RouteModels;
|
||||
}
|
||||
|
||||
public void OnProvidersExecuted(ActionDescriptorProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
private void AddActionDescriptors(IList<ActionDescriptor> actions, PageApplicationModel model)
|
||||
private void AddActionDescriptors(IList<ActionDescriptor> actions, PageRouteModel model)
|
||||
{
|
||||
for (var i = 0; i < _pagesOptions.Conventions.Count; i++)
|
||||
for (var i = 0; i < _pagesOptions.RouteModelConventions.Count; i++)
|
||||
{
|
||||
_pagesOptions.Conventions[i].Apply(model);
|
||||
}
|
||||
|
||||
var filters = new List<FilterDescriptor>(_mvcOptions.Filters.Count + model.Filters.Count);
|
||||
for (var i = 0; i < _mvcOptions.Filters.Count; i++)
|
||||
{
|
||||
filters.Add(new FilterDescriptor(_mvcOptions.Filters[i], FilterScope.Global));
|
||||
}
|
||||
|
||||
for (var i = 0; i < model.Filters.Count; i++)
|
||||
{
|
||||
filters.Add(new FilterDescriptor(model.Filters[i], FilterScope.Action));
|
||||
_pagesOptions.RouteModelConventions[i].Apply(model);
|
||||
}
|
||||
|
||||
foreach (var selector in model.Selectors)
|
||||
|
|
@ -92,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
SuppressPathMatching = selector.AttributeRouteModel.SuppressPathMatching,
|
||||
},
|
||||
DisplayName = $"Page: {model.ViewEnginePath}",
|
||||
FilterDescriptors = filters,
|
||||
FilterDescriptors = Array.Empty<FilterDescriptor>(),
|
||||
Properties = new Dictionary<object, object>(model.Properties),
|
||||
RelativePath = model.RelativePath,
|
||||
RouteValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class AuthorizationPageApplicationModelProvider : IPageApplicationModelProvider
|
||||
{
|
||||
private readonly IAuthorizationPolicyProvider _policyProvider;
|
||||
|
||||
public AuthorizationPageApplicationModelProvider(IAuthorizationPolicyProvider policyProvider)
|
||||
{
|
||||
_policyProvider = policyProvider;
|
||||
}
|
||||
|
||||
// The order is set to execute after the DefaultPageApplicationModelProvider.
|
||||
public int Order => -1000 + 10;
|
||||
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var pageModel = context.PageApplicationModel;
|
||||
var authorizeData = pageModel.HandlerTypeAttributes.OfType<IAuthorizeData>().ToArray();
|
||||
if (authorizeData.Length > 0)
|
||||
{
|
||||
pageModel.Filters.Add(AuthorizationApplicationModelProvider.GetFilter(_policyProvider, authorizeData));
|
||||
}
|
||||
foreach (var attribute in pageModel.HandlerTypeAttributes.OfType<IAllowAnonymous>())
|
||||
{
|
||||
pageModel.Filters.Add(new AllowAnonymousFilter());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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.Mvc.ApplicationModels;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class AutoValidateAntiforgeryPageApplicationModelProvider : IPageApplicationModelProvider
|
||||
{
|
||||
// The order is set to execute after the DefaultPageApplicationModelProvider.
|
||||
public int Order => -1000 + 10;
|
||||
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var pageApplicationModel = context.PageApplicationModel;
|
||||
|
||||
// Always require an antiforgery token on post
|
||||
pageApplicationModel.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
// 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 System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="CompiledPageActionDescriptor"/> from an <see cref="PageApplicationModel"/>.
|
||||
/// </summary>
|
||||
public static class CompiledPageActionDescriptorBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CompiledPageActionDescriptor"/> from the specified <paramref name="applicationModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="applicationModel">The <see cref="PageApplicationModel"/>.</param>
|
||||
/// <returns>The <see cref="CompiledPageActionDescriptor"/>.</returns>
|
||||
public static CompiledPageActionDescriptor Build(PageApplicationModel applicationModel)
|
||||
{
|
||||
var boundProperties = CreateBoundProperties(applicationModel);
|
||||
var filters = applicationModel.Filters
|
||||
.Select(f => new FilterDescriptor(f, FilterScope.Action))
|
||||
.ToArray();
|
||||
var handlerMethods = CreateHandlerMethods(applicationModel);
|
||||
|
||||
var actionDescriptor = applicationModel.ActionDescriptor;
|
||||
return new CompiledPageActionDescriptor(actionDescriptor)
|
||||
{
|
||||
ActionConstraints = actionDescriptor.ActionConstraints,
|
||||
AttributeRouteInfo = actionDescriptor.AttributeRouteInfo,
|
||||
BoundProperties = boundProperties,
|
||||
FilterDescriptors = filters,
|
||||
HandlerMethods = handlerMethods,
|
||||
HandlerTypeInfo = applicationModel.HandlerType,
|
||||
ModelTypeInfo = applicationModel.ModelType,
|
||||
RouteValues = actionDescriptor.RouteValues,
|
||||
PageTypeInfo = applicationModel.PageType,
|
||||
Properties = applicationModel.Properties,
|
||||
};
|
||||
}
|
||||
|
||||
// Internal for unit testing
|
||||
internal static HandlerMethodDescriptor[] CreateHandlerMethods(PageApplicationModel applicationModel)
|
||||
{
|
||||
var handlerModels = applicationModel.HandlerMethods;
|
||||
var handlerDescriptors = new HandlerMethodDescriptor[handlerModels.Count];
|
||||
|
||||
for (var i = 0; i < handlerDescriptors.Length; i++)
|
||||
{
|
||||
var handlerModel = handlerModels[i];
|
||||
|
||||
handlerDescriptors[i] = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = handlerModel.HttpMethod,
|
||||
Name = handlerModel.HandlerName,
|
||||
MethodInfo = handlerModel.MethodInfo,
|
||||
Parameters = CreateHandlerParameters(handlerModel),
|
||||
};
|
||||
}
|
||||
|
||||
return handlerDescriptors;
|
||||
}
|
||||
|
||||
// internal for unit testing
|
||||
internal static HandlerParameterDescriptor[] CreateHandlerParameters(PageHandlerModel handlerModel)
|
||||
{
|
||||
var methodParameters = handlerModel.Parameters;
|
||||
var parameters = new HandlerParameterDescriptor[methodParameters.Count];
|
||||
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameterModel = methodParameters[i];
|
||||
|
||||
parameters[i] = new HandlerParameterDescriptor
|
||||
{
|
||||
BindingInfo = parameterModel.BindingInfo,
|
||||
Name = parameterModel.ParameterName,
|
||||
ParameterInfo = parameterModel.ParameterInfo,
|
||||
ParameterType = parameterModel.ParameterInfo.ParameterType,
|
||||
};
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
// internal for unit testing
|
||||
internal static PageBoundPropertyDescriptor[] CreateBoundProperties(PageApplicationModel applicationModel)
|
||||
{
|
||||
var results = new List<PageBoundPropertyDescriptor>();
|
||||
var properties = applicationModel.HandlerProperties;
|
||||
for (var i = 0; i < properties.Count; i++)
|
||||
{
|
||||
var propertyModel = properties[i];
|
||||
|
||||
// Only add properties which are explicitly marked to bind.
|
||||
if (propertyModel.BindingInfo == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var descriptor = new PageBoundPropertyDescriptor
|
||||
{
|
||||
Property = propertyModel.PropertyInfo,
|
||||
Name = propertyModel.PropertyName,
|
||||
BindingInfo = propertyModel.BindingInfo,
|
||||
ParameterType = propertyModel.PropertyInfo.PropertyType,
|
||||
};
|
||||
|
||||
results.Add(descriptor);
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,14 +12,14 @@ using Microsoft.Extensions.Options;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class CompiledPageApplicationModelProvider : IPageApplicationModelProvider
|
||||
public class CompiledPageRouteModelProvider : IPageRouteModelProvider
|
||||
{
|
||||
private readonly object _cacheLock = new object();
|
||||
private readonly ApplicationPartManager _applicationManager;
|
||||
private readonly RazorPagesOptions _pagesOptions;
|
||||
private List<PageApplicationModel> _cachedApplicationModels;
|
||||
private List<PageRouteModel> _cachedModels;
|
||||
|
||||
public CompiledPageApplicationModelProvider(
|
||||
public CompiledPageRouteModelProvider(
|
||||
ApplicationPartManager applicationManager,
|
||||
IOptions<RazorPagesOptions> pagesOptionsAccessor)
|
||||
{
|
||||
|
|
@ -29,17 +29,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
public int Order => -1000;
|
||||
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
public void OnProvidersExecuting(PageRouteModelProviderContext context)
|
||||
{
|
||||
EnsureCache();
|
||||
for (var i = 0; i < _cachedApplicationModels.Count; i++)
|
||||
for (var i = 0; i < _cachedModels.Count; i++)
|
||||
{
|
||||
var pageModel = _cachedApplicationModels[i];
|
||||
context.Results.Add(new PageApplicationModel(pageModel));
|
||||
var pageModel = _cachedModels[i];
|
||||
context.RouteModels.Add(new PageRouteModel(pageModel));
|
||||
}
|
||||
}
|
||||
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
public void OnProvidersExecuted(PageRouteModelProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
lock (_cacheLock)
|
||||
{
|
||||
if (_cachedApplicationModels != null)
|
||||
if (_cachedModels != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
rootDirectory = rootDirectory + "/";
|
||||
}
|
||||
|
||||
var cachedApplicationModels = new List<PageApplicationModel>();
|
||||
var cachedApplicationModels = new List<PageRouteModel>();
|
||||
foreach (var viewDescriptor in GetViewDescriptors(_applicationManager))
|
||||
{
|
||||
if (!viewDescriptor.RelativePath.StartsWith(rootDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
|
|
@ -67,14 +67,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
|
||||
var viewEnginePath = GetViewEnginePath(rootDirectory, viewDescriptor.RelativePath);
|
||||
var model = new PageApplicationModel(viewDescriptor.RelativePath, viewEnginePath);
|
||||
var model = new PageRouteModel(viewDescriptor.RelativePath, viewEnginePath);
|
||||
var pageAttribute = (RazorPageAttribute)viewDescriptor.ViewAttribute;
|
||||
PageSelectorModel.PopulateDefaults(model, pageAttribute.RouteTemplate);
|
||||
|
||||
cachedApplicationModels.Add(model);
|
||||
}
|
||||
|
||||
_cachedApplicationModels = cachedApplicationModels;
|
||||
_cachedModels = cachedApplicationModels;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
// 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.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class DefaultPageApplicationModelProvider : IPageApplicationModelProvider
|
||||
{
|
||||
private const string ModelPropertyName = "Model";
|
||||
private readonly FilterCollection _globalFilters;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="DefaultPageApplicationModelProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="mvcOptions"></param>
|
||||
public DefaultPageApplicationModelProvider(IOptions<MvcOptions> mvcOptions)
|
||||
{
|
||||
_globalFilters = mvcOptions.Value.Filters;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Order => -1000;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
context.PageApplicationModel = CreateModel(context.ActionDescriptor, context.PageType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="PageApplicationModel"/> for the given <paramref name="pageTypeInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="actionDescriptor">The <see cref="PageActionDescriptor"/>.</param>
|
||||
/// <param name="pageTypeInfo">The <see cref="TypeInfo"/>.</param>
|
||||
/// <returns>A <see cref="PageApplicationModel"/> for the given <see cref="TypeInfo"/>.</returns>
|
||||
protected virtual PageApplicationModel CreateModel(
|
||||
PageActionDescriptor actionDescriptor,
|
||||
TypeInfo pageTypeInfo)
|
||||
{
|
||||
if (actionDescriptor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actionDescriptor));
|
||||
}
|
||||
|
||||
if (pageTypeInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(pageTypeInfo));
|
||||
}
|
||||
|
||||
// Pages always have a model type. If it's not set explicitly by the developer using
|
||||
// @model, it will be the same as the page type.
|
||||
var modelTypeInfo = pageTypeInfo.GetProperty(ModelPropertyName)?.PropertyType?.GetTypeInfo();
|
||||
|
||||
// Now we want to find the handler methods. If the model defines any handlers, then we'll use those,
|
||||
// otherwise look at the page itself (unless the page IS the model, in which case we already looked).
|
||||
TypeInfo handlerType;
|
||||
|
||||
var handlerModels = modelTypeInfo == null ? null : CreateHandlerModels(modelTypeInfo);
|
||||
if (handlerModels?.Count > 0)
|
||||
{
|
||||
handlerType = modelTypeInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
handlerType = pageTypeInfo.GetTypeInfo();
|
||||
handlerModels = CreateHandlerModels(pageTypeInfo);
|
||||
}
|
||||
|
||||
var handlerTypeAttributes = handlerType.GetCustomAttributes(inherit: true);
|
||||
var pageModel = new PageApplicationModel(
|
||||
actionDescriptor,
|
||||
handlerType,
|
||||
handlerTypeAttributes)
|
||||
{
|
||||
PageType = pageTypeInfo,
|
||||
ModelType = modelTypeInfo,
|
||||
};
|
||||
|
||||
for (var i = 0; i < handlerModels.Count; i++)
|
||||
{
|
||||
var handlerModel = handlerModels[i];
|
||||
handlerModel.Page = pageModel;
|
||||
pageModel.HandlerMethods.Add(handlerModel);
|
||||
}
|
||||
|
||||
PopulateHandlerProperties(pageModel);
|
||||
|
||||
for (var i = 0; i < _globalFilters.Count; i++)
|
||||
{
|
||||
pageModel.Filters.Add(_globalFilters[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < handlerTypeAttributes.Length; i++)
|
||||
{
|
||||
if (handlerTypeAttributes[i] is IFilterMetadata filter)
|
||||
{
|
||||
pageModel.Filters.Add(filter);
|
||||
}
|
||||
}
|
||||
|
||||
return pageModel;
|
||||
}
|
||||
|
||||
// Internal for unit testing
|
||||
internal void PopulateHandlerProperties(PageApplicationModel pageModel)
|
||||
{
|
||||
var properties = PropertyHelper.GetVisibleProperties(pageModel.HandlerType.AsType());
|
||||
for (var i = 0; i < properties.Length; i++)
|
||||
{
|
||||
var propertyModel = CreatePropertyModel(properties[i].Property);
|
||||
if (propertyModel != null)
|
||||
{
|
||||
propertyModel.Page = pageModel;
|
||||
pageModel.HandlerProperties.Add(propertyModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for unit testing
|
||||
internal IList<PageHandlerModel> CreateHandlerModels(TypeInfo handlerTypeInfo)
|
||||
{
|
||||
var methods = handlerTypeInfo.GetMethods();
|
||||
var results = new List<PageHandlerModel>();
|
||||
|
||||
for (var i = 0; i < methods.Length; i++)
|
||||
{
|
||||
var handler = CreateHandlerModel(methods[i]);
|
||||
if (handler != null)
|
||||
{
|
||||
results.Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="PageHandlerModel"/> for the specified <paramref name="method"/>.s
|
||||
/// </summary>
|
||||
/// <param name="method">The <see cref="MethodInfo"/>.</param>
|
||||
/// <returns>The <see cref="PageHandlerModel"/>.</returns>
|
||||
protected virtual PageHandlerModel CreateHandlerModel(MethodInfo method)
|
||||
{
|
||||
if (method == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(method));
|
||||
}
|
||||
|
||||
if (!IsHandler(method))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (method.IsDefined(typeof(NonHandlerAttribute)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (method.DeclaringType.GetTypeInfo().IsDefined(typeof(PagesBaseClassAttribute)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!TryParseHandlerMethod(method.Name, out var httpMethod, out var handlerName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var handlerModel = new PageHandlerModel(
|
||||
method,
|
||||
method.GetCustomAttributes(inherit: true))
|
||||
{
|
||||
Name = method.Name,
|
||||
HandlerName = handlerName,
|
||||
HttpMethod = httpMethod,
|
||||
};
|
||||
|
||||
var methodParameters = handlerModel.MethodInfo.GetParameters();
|
||||
|
||||
for (var i = 0; i < methodParameters.Length; i++)
|
||||
{
|
||||
var parameter = methodParameters[i];
|
||||
var parameterModel = CreateParameterModel(parameter);
|
||||
parameterModel.Handler = handlerModel;
|
||||
|
||||
handlerModel.Parameters.Add(parameterModel);
|
||||
}
|
||||
|
||||
return handlerModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="PageParameterModel"/> for the specified <paramref name="parameter"/>.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The <see cref="ParameterInfo"/>.</param>
|
||||
/// <returns>The <see cref="PageParameterModel"/>.</returns>
|
||||
protected virtual PageParameterModel CreateParameterModel(ParameterInfo parameter)
|
||||
{
|
||||
if (parameter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameter));
|
||||
}
|
||||
|
||||
return new PageParameterModel(parameter, parameter.GetCustomAttributes(inherit: true))
|
||||
{
|
||||
BindingInfo = BindingInfo.GetBindingInfo(parameter.GetCustomAttributes()),
|
||||
ParameterName = parameter.Name,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="PagePropertyModel"/> for the <paramref name="property"/>.
|
||||
/// </summary>
|
||||
/// <param name="property">The <see cref="PropertyInfo"/>.</param>
|
||||
/// <returns>The <see cref="PagePropertyModel"/>.</returns>
|
||||
protected virtual PagePropertyModel CreatePropertyModel(PropertyInfo property)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(property));
|
||||
}
|
||||
|
||||
var attributes = property.GetCustomAttributes(inherit: true);
|
||||
var bindingInfo = BindingInfo.GetBindingInfo(attributes);
|
||||
|
||||
var model = new PagePropertyModel(property, property.GetCustomAttributes(inherit: true))
|
||||
{
|
||||
PropertyName = property.Name,
|
||||
BindingInfo = bindingInfo,
|
||||
};
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <paramref name="methodInfo"/> is a handler.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">The <see cref="MethodInfo"/>.</param>
|
||||
/// <returns><c>true</c> if the <paramref name="methodInfo"/> is a handler. Otherwise <c>false</c>.</returns>
|
||||
/// <remarks>
|
||||
/// Override this method to provide custom logic to determine which methods are considered handlers.
|
||||
/// </remarks>
|
||||
protected virtual bool IsHandler(MethodInfo methodInfo)
|
||||
{
|
||||
// The SpecialName bit is set to flag members that are treated in a special way by some compilers
|
||||
// (such as property accessors and operator overloading methods).
|
||||
if (methodInfo.IsSpecialName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Overriden methods from Object class, e.g. Equals(Object), GetHashCode(), etc., are not valid.
|
||||
if (methodInfo.GetBaseDefinition().DeclaringType == typeof(object))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsStatic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsConstructor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsGenericMethod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return methodInfo.IsPublic;
|
||||
}
|
||||
|
||||
internal static bool TryParseHandlerMethod(string methodName, out string httpMethod, out string handler)
|
||||
{
|
||||
httpMethod = null;
|
||||
handler = null;
|
||||
|
||||
// Handler method names always start with "On"
|
||||
if (!methodName.StartsWith("On") || methodName.Length <= "On".Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now we parse the method name according to our conventions to determine the required HTTP method
|
||||
// and optional 'handler name'.
|
||||
//
|
||||
// Valid names look like:
|
||||
// - OnGet
|
||||
// - OnPost
|
||||
// - OnFooBar
|
||||
// - OnTraceAsync
|
||||
// - OnPostEditAsync
|
||||
|
||||
var start = "On".Length;
|
||||
var length = methodName.Length;
|
||||
if (methodName.EndsWith("Async", StringComparison.Ordinal))
|
||||
{
|
||||
length -= "Async".Length;
|
||||
}
|
||||
|
||||
if (start == length)
|
||||
{
|
||||
// There are no additional characters. This is "On" or "OnAsync".
|
||||
return false;
|
||||
}
|
||||
|
||||
// The http method follows "On" and is required to be at least one character. We use casing
|
||||
// to determine where it ends.
|
||||
var handlerNameStart = start + 1;
|
||||
for (; handlerNameStart < length; handlerNameStart++)
|
||||
{
|
||||
if (char.IsUpper(methodName[handlerNameStart]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
httpMethod = methodName.Substring(start, handlerNameStart - start);
|
||||
|
||||
// The handler name follows the http method and is optional. It includes everything up to the end
|
||||
// excluding the "Async" suffix (if present).
|
||||
handler = handlerNameStart == length ? null : methodName.Substring(handlerNameStart, length - handlerNameStart);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,267 +3,61 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class DefaultPageLoader : IPageLoader
|
||||
{
|
||||
private const string ModelPropertyName = "Model";
|
||||
|
||||
private readonly IPageApplicationModelProvider[] _applicationModelProviders;
|
||||
private readonly IViewCompilerProvider _viewCompilerProvider;
|
||||
private readonly RazorPagesOptions _options;
|
||||
|
||||
public DefaultPageLoader(IViewCompilerProvider viewCompilerProvider)
|
||||
public DefaultPageLoader(
|
||||
IEnumerable<IPageApplicationModelProvider> applicationModelProviders,
|
||||
IViewCompilerProvider viewCompilerProvider,
|
||||
IOptions<RazorPagesOptions> pageOptions)
|
||||
{
|
||||
_applicationModelProviders = applicationModelProviders.ToArray();
|
||||
_viewCompilerProvider = viewCompilerProvider;
|
||||
_options = pageOptions.Value;
|
||||
}
|
||||
|
||||
private IViewCompiler Compiler => _viewCompilerProvider.GetCompiler();
|
||||
|
||||
public CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor)
|
||||
{
|
||||
if (actionDescriptor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actionDescriptor));
|
||||
}
|
||||
|
||||
var compileTask = Compiler.CompileAsync(actionDescriptor.RelativePath);
|
||||
var viewDescriptor = compileTask.GetAwaiter().GetResult();
|
||||
var pageAttribute = (RazorPageAttribute)viewDescriptor.ViewAttribute;
|
||||
|
||||
return CreateDescriptor(actionDescriptor, pageAttribute);
|
||||
}
|
||||
|
||||
// Internal for unit testing
|
||||
internal static CompiledPageActionDescriptor CreateDescriptor(
|
||||
PageActionDescriptor actionDescriptor,
|
||||
RazorPageAttribute pageAttribute)
|
||||
{
|
||||
var pageType = pageAttribute.ViewType.GetTypeInfo();
|
||||
|
||||
// Pages always have a model type. If it's not set explicitly by the developer using
|
||||
// @model, it will be the same as the page type.
|
||||
var modelType = pageAttribute.ViewType.GetProperty(ModelPropertyName)?.PropertyType?.GetTypeInfo();
|
||||
|
||||
// Now we want to find the handler methods. If the model defines any handlers, then we'll use those,
|
||||
// otherwise look at the page itself (unless the page IS the model, in which case we already looked).
|
||||
TypeInfo handlerType;
|
||||
|
||||
var handlerMethods = modelType == null ? null : CreateHandlerMethods(modelType);
|
||||
if (handlerMethods?.Length > 0)
|
||||
var context = new PageApplicationModelProviderContext(actionDescriptor, pageAttribute.ViewType.GetTypeInfo());
|
||||
for (var i = 0; i < _applicationModelProviders.Length; i++)
|
||||
{
|
||||
handlerType = modelType;
|
||||
}
|
||||
else
|
||||
{
|
||||
handlerType = pageType;
|
||||
handlerMethods = CreateHandlerMethods(pageType);
|
||||
_applicationModelProviders[i].OnProvidersExecuting(context);
|
||||
}
|
||||
|
||||
var boundProperties = CreateBoundProperties(handlerType);
|
||||
|
||||
return new CompiledPageActionDescriptor(actionDescriptor)
|
||||
for (var i = _applicationModelProviders.Length - 1; i >= 0; i--)
|
||||
{
|
||||
ActionConstraints = actionDescriptor.ActionConstraints,
|
||||
AttributeRouteInfo = actionDescriptor.AttributeRouteInfo,
|
||||
BoundProperties = boundProperties,
|
||||
FilterDescriptors = actionDescriptor.FilterDescriptors,
|
||||
HandlerMethods = handlerMethods,
|
||||
HandlerTypeInfo = handlerType,
|
||||
ModelTypeInfo = modelType,
|
||||
RouteValues = actionDescriptor.RouteValues,
|
||||
PageTypeInfo = pageType,
|
||||
Properties = actionDescriptor.Properties,
|
||||
};
|
||||
}
|
||||
|
||||
internal static HandlerMethodDescriptor[] CreateHandlerMethods(TypeInfo type)
|
||||
{
|
||||
var methods = type.GetMethods();
|
||||
var results = new List<HandlerMethodDescriptor>();
|
||||
|
||||
for (var i = 0; i < methods.Length; i++)
|
||||
{
|
||||
var method = methods[i];
|
||||
if (!IsValidHandlerMethod(method))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.IsDefined(typeof(NonHandlerAttribute)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.DeclaringType.GetTypeInfo().IsDefined(typeof(PagesBaseClassAttribute)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryParseHandlerMethod(method.Name, out var httpMethod, out var handler))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var parameters = CreateHandlerParameters(method);
|
||||
|
||||
var handlerMethodDescriptor = new HandlerMethodDescriptor()
|
||||
{
|
||||
MethodInfo = method,
|
||||
Name = handler,
|
||||
HttpMethod = httpMethod,
|
||||
Parameters = parameters,
|
||||
};
|
||||
|
||||
results.Add(handlerMethodDescriptor);
|
||||
_applicationModelProviders[i].OnProvidersExecuted(context);
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryParseHandlerMethod(string methodName, out string httpMethod, out string handler)
|
||||
{
|
||||
httpMethod = null;
|
||||
handler = null;
|
||||
|
||||
// Handler method names always start with "On"
|
||||
if (!methodName.StartsWith("On") || methodName.Length <= "On".Length)
|
||||
for (var i = 0; i < _options.ApplicationModelConventions.Count; i++)
|
||||
{
|
||||
return false;
|
||||
_options.ApplicationModelConventions[i].Apply(context.PageApplicationModel);
|
||||
}
|
||||
|
||||
// Now we parse the method name according to our conventions to determine the required HTTP method
|
||||
// and optional 'handler name'.
|
||||
//
|
||||
// Valid names look like:
|
||||
// - OnGet
|
||||
// - OnPost
|
||||
// - OnFooBar
|
||||
// - OnTraceAsync
|
||||
// - OnPostEditAsync
|
||||
|
||||
var start = "On".Length;
|
||||
var length = methodName.Length;
|
||||
if (methodName.EndsWith("Async", StringComparison.Ordinal))
|
||||
{
|
||||
length -= "Async".Length;
|
||||
}
|
||||
|
||||
if (start == length)
|
||||
{
|
||||
// There are no additional characters. This is "On" or "OnAsync".
|
||||
return false;
|
||||
}
|
||||
|
||||
// The http method follows "On" and is required to be at least one character. We use casing
|
||||
// to determine where it ends.
|
||||
var handlerNameStart = start + 1;
|
||||
for (; handlerNameStart < length; handlerNameStart++)
|
||||
{
|
||||
if (char.IsUpper(methodName[handlerNameStart]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
httpMethod = methodName.Substring(start, handlerNameStart - start);
|
||||
|
||||
// The handler name follows the http method and is optional. It includes everything up to the end
|
||||
// excluding the "Async" suffix (if present).
|
||||
handler = handlerNameStart == length ? null : methodName.Substring(handlerNameStart, length - handlerNameStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsValidHandlerMethod(MethodInfo methodInfo)
|
||||
{
|
||||
// The SpecialName bit is set to flag members that are treated in a special way by some compilers
|
||||
// (such as property accessors and operator overloading methods).
|
||||
if (methodInfo.IsSpecialName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Overriden methods from Object class, e.g. Equals(Object), GetHashCode(), etc., are not valid.
|
||||
if (methodInfo.GetBaseDefinition().DeclaringType == typeof(object))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsStatic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsConstructor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsGenericMethod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return methodInfo.IsPublic;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static HandlerParameterDescriptor[] CreateHandlerParameters(MethodInfo methodInfo)
|
||||
{
|
||||
var methodParameters = methodInfo.GetParameters();
|
||||
var parameters = new HandlerParameterDescriptor[methodParameters.Length];
|
||||
|
||||
for (var i = 0; i < methodParameters.Length; i++)
|
||||
{
|
||||
var parameter = methodParameters[i];
|
||||
|
||||
parameters[i] = new HandlerParameterDescriptor()
|
||||
{
|
||||
BindingInfo = BindingInfo.GetBindingInfo(parameter.GetCustomAttributes()),
|
||||
Name = parameter.Name,
|
||||
ParameterInfo = parameter,
|
||||
ParameterType = parameter.ParameterType,
|
||||
};
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static PageBoundPropertyDescriptor[] CreateBoundProperties(TypeInfo type)
|
||||
{
|
||||
var properties = PropertyHelper.GetVisibleProperties(type.AsType());
|
||||
|
||||
var results = new List<PageBoundPropertyDescriptor>();
|
||||
for (var i = 0; i < properties.Length; i++)
|
||||
{
|
||||
var property = properties[i];
|
||||
var bindingInfo = BindingInfo.GetBindingInfo(property.Property.GetCustomAttributes());
|
||||
|
||||
// If there's no binding info then that means there are no model binding attributes on the
|
||||
// property. So we won't bind this property.
|
||||
if (bindingInfo == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var descriptor = new PageBoundPropertyDescriptor()
|
||||
{
|
||||
BindingInfo = bindingInfo,
|
||||
Name = property.Name,
|
||||
Property = property.Property,
|
||||
ParameterType = property.Property.PropertyType,
|
||||
};
|
||||
|
||||
results.Add(descriptor);
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
return CompiledPageActionDescriptorBuilder.Build(context.PageApplicationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -94,11 +94,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
|
||||
var cache = CurrentCache;
|
||||
PageActionInvokerCacheEntry cacheEntry;
|
||||
|
||||
IFilterMetadata[] filters;
|
||||
if (!cache.Entries.TryGetValue(actionDescriptor, out cacheEntry))
|
||||
if (!cache.Entries.TryGetValue(actionDescriptor, out var cacheEntry))
|
||||
{
|
||||
actionContext.ActionDescriptor = _loader.Load(actionDescriptor);
|
||||
|
||||
var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, actionContext);
|
||||
filters = filterFactoryResult.Filters;
|
||||
cacheEntry = CreateCacheEntry(context, filterFactoryResult.CacheableFilters);
|
||||
|
|
@ -166,8 +166,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
ActionInvokerProviderContext context,
|
||||
FilterItem[] cachedFilters)
|
||||
{
|
||||
var actionDescriptor = (PageActionDescriptor)context.ActionContext.ActionDescriptor;
|
||||
var compiledActionDescriptor = _loader.Load(actionDescriptor);
|
||||
var compiledActionDescriptor = (CompiledPageActionDescriptor)context.ActionContext.ActionDescriptor;
|
||||
|
||||
var viewDataFactory = ViewDataDictionaryFactory.CreateFactory(compiledActionDescriptor.ModelTypeInfo);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +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 Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class PageFilterApplicationModelProvider : IPageApplicationModelProvider
|
||||
{
|
||||
/// <remarks>This order ensures that <see cref="PageFilterApplicationModelProvider"/> runs after
|
||||
/// <see cref="RazorProjectPageApplicationModelProvider"/> and <see cref="CompiledPageApplicationModelProvider"/>.
|
||||
/// </remarks>
|
||||
public int Order => -1000 + 10;
|
||||
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
for (var i = 0; i < context.Results.Count; i++)
|
||||
{
|
||||
var pageApplicationModel = context.Results[i];
|
||||
|
||||
// Support for [TempData] on properties
|
||||
pageApplicationModel.Filters.Add(new PageSaveTempDataPropertyFilterFactory());
|
||||
|
||||
// Always require an antiforgery token on post
|
||||
pageApplicationModel.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
private const string IndexFileName = "Index.cshtml";
|
||||
|
||||
public static void PopulateDefaults(PageApplicationModel model, string routeTemplate)
|
||||
public static void PopulateDefaults(PageRouteModel model, string routeTemplate)
|
||||
{
|
||||
if (AttributeRouteModel.IsOverridePattern(routeTemplate))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,29 +9,29 @@ using Microsoft.Extensions.Options;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class RazorProjectPageApplicationModelProvider : IPageApplicationModelProvider
|
||||
public class RazorProjectPageRouteModelProvider : IPageRouteModelProvider
|
||||
{
|
||||
private readonly RazorProject _project;
|
||||
private readonly RazorPagesOptions _pagesOptions;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public RazorProjectPageApplicationModelProvider(
|
||||
public RazorProjectPageRouteModelProvider(
|
||||
RazorProject razorProject,
|
||||
IOptions<RazorPagesOptions> pagesOptionsAccessor,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
_project = razorProject;
|
||||
_pagesOptions = pagesOptionsAccessor.Value;
|
||||
_logger = loggerFactory.CreateLogger<RazorProjectPageApplicationModelProvider>();
|
||||
_logger = loggerFactory.CreateLogger<RazorProjectPageRouteModelProvider>();
|
||||
}
|
||||
|
||||
public int Order => -1000;
|
||||
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
public void OnProvidersExecuted(PageRouteModelProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
public void OnProvidersExecuting(PageRouteModelProviderContext context)
|
||||
{
|
||||
foreach (var item in _project.EnumerateItems(_pagesOptions.RootDirectory))
|
||||
{
|
||||
|
|
@ -47,12 +47,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
continue;
|
||||
}
|
||||
|
||||
var pageApplicationModel = new PageApplicationModel(
|
||||
var routeModel = new PageRouteModel(
|
||||
relativePath: item.CombinedPath,
|
||||
viewEnginePath: item.PathWithoutExtension);
|
||||
PageSelectorModel.PopulateDefaults(pageApplicationModel, routeTemplate);
|
||||
PageSelectorModel.PopulateDefaults(routeModel, routeTemplate);
|
||||
|
||||
context.Results.Add(pageApplicationModel);
|
||||
context.RouteModels.Add(routeModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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.Mvc.ApplicationModels;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class TempDataFilterPageApplicationModelProvider : IPageApplicationModelProvider
|
||||
{
|
||||
// The order is set to execute after the DefaultPageApplicationModelProvider.
|
||||
public int Order => -1000 + 10;
|
||||
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var pageApplicationModel = context.PageApplicationModel;
|
||||
|
||||
// Support for [TempData] on properties
|
||||
pageApplicationModel.Filters.Add(new PageSaveTempDataPropertyFilterFactory());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor\Microsoft.AspNetCore.Mvc.Razor.csproj" />
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
|
|
@ -15,10 +17,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
private string _root = "/Pages";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IPageApplicationModelConvention"/> instances that will be applied to
|
||||
/// Gets a list of <see cref="IPageRouteModelConvention"/> instances that will be applied to
|
||||
/// the <see cref="PageModel"/> when discovering Razor Pages.
|
||||
/// </summary>
|
||||
public IList<IPageApplicationModelConvention> Conventions { get; } = new List<IPageApplicationModelConvention>();
|
||||
public IList<IPageRouteModelConvention> RouteModelConventions { get; } = new List<IPageRouteModelConvention>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IPageRouteModelConvention"/> instances that will be applied to
|
||||
/// the <see cref="PageModel"/> when discovering Razor Pages.
|
||||
/// </summary>
|
||||
public IList<IPageApplicationModelConvention> ApplicationModelConventions { get; } = new List<IPageApplicationModelConvention>();
|
||||
|
||||
/// <summary>
|
||||
/// Application relative path used as the root of discovery for Razor Page files.
|
||||
|
|
|
|||
|
|
@ -1050,6 +1050,30 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP
|
|||
Assert.StartsWith(expected, response.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthFiltersAppliedToPageModel_AreExecuted()
|
||||
{
|
||||
// Act
|
||||
var response = await Client.GetAsync("/ModelWithAuthFilter");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
Assert.Equal("/Login?ReturnUrl=%2FModelWithAuthFilter", response.Headers.Location.PathAndQuery);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PageFiltersAppliedToPageModel_AreExecuted()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "Hello from OnGetEdit";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetStringAsync("/ModelWithPageFilter");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, response.Trim());
|
||||
}
|
||||
|
||||
private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
|
||||
{
|
||||
var getResponse = await Client.GetAsync(request.RequestUri);
|
||||
|
|
|
|||
|
|
@ -115,6 +115,16 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuthFolder", response.Headers.Location.PathAndQuery);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthConvention_AppliedToFolders_CanByOverridenByFiltersOnModel()
|
||||
{
|
||||
// Act
|
||||
var response = await Client.GetStringAsync("/Conventions/AuthFolder/AnonymousViaModel");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Hello from Anonymous", response.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewStart_IsDiscoveredWhenRootDirectoryIsSpecified()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,21 +18,23 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
// Arrange
|
||||
var services = new ServiceCollection().AddOptions();
|
||||
var expected = Mock.Of<IPageApplicationModelConvention>();
|
||||
var applicationModelConvention = Mock.Of<IPageApplicationModelConvention>();
|
||||
var routeModelConvention = Mock.Of<IPageRouteModelConvention>();
|
||||
var builder = new MvcBuilder(services, new ApplicationPartManager());
|
||||
builder.AddRazorPagesOptions(options =>
|
||||
{
|
||||
options.Conventions.Add(expected);
|
||||
options.ApplicationModelConventions.Add(applicationModelConvention);
|
||||
options.RouteModelConventions.Add(routeModelConvention);
|
||||
});
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
var accessor = serviceProvider.GetRequiredService<IOptions<RazorPagesOptions>>();
|
||||
|
||||
// Act
|
||||
var conventions = accessor.Value.Conventions;
|
||||
// Act & Assert
|
||||
var conventions = accessor.Value.ApplicationModelConventions;
|
||||
|
||||
// Assert
|
||||
Assert.Collection(conventions,
|
||||
convention => Assert.Same(expected, convention));
|
||||
convention => Assert.Same(applicationModelConvention, convention));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Reflection;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
|
|
@ -21,9 +22,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var options = new RazorPagesOptions();
|
||||
var models = new[]
|
||||
{
|
||||
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -44,9 +45,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var options = new RazorPagesOptions();
|
||||
var models = new[]
|
||||
{
|
||||
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -79,9 +80,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var options = new RazorPagesOptions();
|
||||
var models = new[]
|
||||
{
|
||||
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -117,9 +118,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var options = new RazorPagesOptions();
|
||||
var models = new[]
|
||||
{
|
||||
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -146,9 +147,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var options = new RazorPagesOptions();
|
||||
var models = new[]
|
||||
{
|
||||
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -177,9 +178,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var options = new RazorPagesOptions();
|
||||
var models = new[]
|
||||
{
|
||||
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -214,9 +215,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var options = new RazorPagesOptions();
|
||||
var models = new[]
|
||||
{
|
||||
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
new PageApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
|
||||
CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -249,7 +250,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var options = new RazorPagesOptions();
|
||||
var models = new[]
|
||||
{
|
||||
new PageApplicationModel("/Pages/Index.cshtml", "/Index.cshtml")
|
||||
new PageRouteModel("/Pages/Index.cshtml", "/Index.cshtml")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
|
|
@ -257,7 +258,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
CreateSelectorModel(""),
|
||||
}
|
||||
},
|
||||
new PageApplicationModel("/Pages/About.cshtml", "/About.cshtml")
|
||||
new PageRouteModel("/Pages/About.cshtml", "/About.cshtml")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
|
|
@ -316,9 +317,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
};
|
||||
}
|
||||
|
||||
private static void ApplyConventions(RazorPagesOptions options, PageApplicationModel[] models)
|
||||
private static void ApplyConventions(RazorPagesOptions options, PageRouteModel[] models)
|
||||
{
|
||||
foreach (var convention in options.Conventions)
|
||||
foreach (var convention in options.RouteModelConventions)
|
||||
{
|
||||
foreach (var model in models)
|
||||
{
|
||||
|
|
@ -326,5 +327,26 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
}
|
||||
}
|
||||
private static void ApplyConventions(RazorPagesOptions options, PageApplicationModel[] models)
|
||||
{
|
||||
foreach (var convention in options.ApplicationModelConventions)
|
||||
{
|
||||
foreach (var model in models)
|
||||
{
|
||||
convention.Apply(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PageApplicationModel CreateApplicationModel(string relativePath, string viewEnginePath)
|
||||
{
|
||||
var descriptor = new PageActionDescriptor
|
||||
{
|
||||
ViewEnginePath = viewEnginePath,
|
||||
RelativePath = relativePath,
|
||||
};
|
||||
|
||||
return new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,8 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
|
|
@ -22,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
public void GetDescriptors_DoesNotAddDescriptorsIfNoApplicationModelsAreDiscovered()
|
||||
{
|
||||
// Arrange
|
||||
var applicationModelProvider = new TestPageApplicationModelProvider();
|
||||
var applicationModelProvider = new TestPageRouteModelProvider();
|
||||
var provider = new PageActionDescriptorProvider(
|
||||
new[] { applicationModelProvider },
|
||||
GetAccessor<MvcOptions>(),
|
||||
|
|
@ -40,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
public void GetDescriptors_AddsDescriptorsForModelWithSelector()
|
||||
{
|
||||
// Arrange
|
||||
var model = new PageApplicationModel("/Test.cshtml", "/Test")
|
||||
var model = new PageRouteModel("/Test.cshtml", "/Test")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
|
|
@ -53,7 +50,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
}
|
||||
}
|
||||
};
|
||||
var applicationModelProvider = new TestPageApplicationModelProvider(model);
|
||||
var applicationModelProvider = new TestPageRouteModelProvider(model);
|
||||
var provider = new PageActionDescriptorProvider(
|
||||
new[] { applicationModelProvider },
|
||||
GetAccessor<MvcOptions>(),
|
||||
|
|
@ -75,15 +72,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
public void GetDescriptors_AddsActionDescriptorForEachSelector()
|
||||
{
|
||||
// Arrange
|
||||
var applicationModelProvider = new TestPageApplicationModelProvider(
|
||||
new PageApplicationModel("/base-path/Test.cshtml", "/base-path/Test")
|
||||
var applicationModelProvider = new TestPageRouteModelProvider(
|
||||
new PageRouteModel("/base-path/Test.cshtml", "/base-path/Test")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
CreateSelectorModel("base-path/Test/Home")
|
||||
}
|
||||
},
|
||||
new PageApplicationModel("/base-path/Index.cshtml", "/base-path/Index")
|
||||
new PageRouteModel("/base-path/Index.cshtml", "/base-path/Index")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
|
|
@ -91,7 +88,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
CreateSelectorModel("base-path/"),
|
||||
}
|
||||
},
|
||||
new PageApplicationModel("/base-path/Admin/Index.cshtml", "/base-path/Admin/Index")
|
||||
new PageRouteModel("/base-path/Admin/Index.cshtml", "/base-path/Admin/Index")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
|
|
@ -99,7 +96,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
CreateSelectorModel("base-path/Admin"),
|
||||
}
|
||||
},
|
||||
new PageApplicationModel("/base-path/Admin/User.cshtml", "/base-path/Admin/User")
|
||||
new PageRouteModel("/base-path/Admin/User.cshtml", "/base-path/Admin/User")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
|
|
@ -143,8 +140,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
public void GetDescriptors_AddsMultipleDescriptorsForPageWithMultipleSelectors()
|
||||
{
|
||||
// Arrange
|
||||
var applicationModelProvider = new TestPageApplicationModelProvider(
|
||||
new PageApplicationModel("/Catalog/Details/Index.cshtml", "/Catalog/Details/Index")
|
||||
var applicationModelProvider = new TestPageRouteModelProvider(
|
||||
new PageRouteModel("/Catalog/Details/Index.cshtml", "/Catalog/Details/Index")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
|
|
@ -180,142 +177,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_ImplicitFilters()
|
||||
private static PageRouteModel CreateModel()
|
||||
{
|
||||
// Arrange
|
||||
var options = new MvcOptions();
|
||||
var applicationModelProvider = new TestPageApplicationModelProvider(CreateModel());
|
||||
var filterProvider = new PageFilterApplicationModelProvider();
|
||||
var provider = new PageActionDescriptorProvider(
|
||||
new IPageApplicationModelProvider[] { applicationModelProvider, filterProvider },
|
||||
GetAccessor(options),
|
||||
GetRazorPagesOptions());
|
||||
var context = new ActionDescriptorProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var result = Assert.Single(context.Results);
|
||||
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||
Assert.Collection(
|
||||
descriptor.FilterDescriptors,
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||
Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
||||
},
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||
Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filterDescriptor.Filter);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_AddsGlobalFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filter1 = Mock.Of<IFilterMetadata>();
|
||||
var filter2 = Mock.Of<IFilterMetadata>();
|
||||
var options = new MvcOptions();
|
||||
options.Filters.Add(filter1);
|
||||
options.Filters.Add(filter2);
|
||||
var applicationModelProvider = new TestPageApplicationModelProvider(CreateModel());
|
||||
var filterProvider = new PageFilterApplicationModelProvider();
|
||||
var provider = new PageActionDescriptorProvider(
|
||||
new IPageApplicationModelProvider[] { applicationModelProvider, filterProvider },
|
||||
GetAccessor(options),
|
||||
GetRazorPagesOptions());
|
||||
var context = new ActionDescriptorProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var result = Assert.Single(context.Results);
|
||||
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||
Assert.Collection(
|
||||
descriptor.FilterDescriptors,
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
|
||||
Assert.Same(filter1, filterDescriptor.Filter);
|
||||
},
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
|
||||
Assert.Same(filter2, filterDescriptor.Filter);
|
||||
},
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||
Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
||||
},
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||
Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filterDescriptor.Filter);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_AddsFiltersAddedByConvention()
|
||||
{
|
||||
// Arrange
|
||||
var globalFilter = Mock.Of<IFilterMetadata>();
|
||||
var localFilter = Mock.Of<IFilterMetadata>();
|
||||
var options = new MvcOptions();
|
||||
options.Filters.Add(globalFilter);
|
||||
var convention = new Mock<IPageApplicationModelConvention>();
|
||||
convention.Setup(c => c.Apply(It.IsAny<PageApplicationModel>()))
|
||||
.Callback((PageApplicationModel model) =>
|
||||
{
|
||||
model.Filters.Add(localFilter);
|
||||
});
|
||||
var razorOptions = GetRazorPagesOptions();
|
||||
razorOptions.Value.Conventions.Add(convention.Object);
|
||||
var applicationModelProvider = new TestPageApplicationModelProvider(CreateModel());
|
||||
var filterProvider = new PageFilterApplicationModelProvider();
|
||||
var provider = new PageActionDescriptorProvider(
|
||||
new IPageApplicationModelProvider[] { applicationModelProvider, filterProvider },
|
||||
GetAccessor(options),
|
||||
razorOptions);
|
||||
var context = new ActionDescriptorProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var result = Assert.Single(context.Results);
|
||||
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||
Assert.Collection(descriptor.FilterDescriptors,
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
|
||||
Assert.Same(globalFilter, filterDescriptor.Filter);
|
||||
},
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||
Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filterDescriptor.Filter);
|
||||
},
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||
Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filterDescriptor.Filter);
|
||||
},
|
||||
filterDescriptor =>
|
||||
{
|
||||
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||
Assert.Same(localFilter, filterDescriptor.Filter);
|
||||
});
|
||||
}
|
||||
|
||||
private static PageApplicationModel CreateModel()
|
||||
{
|
||||
return new PageApplicationModel("/Home.cshtml", "/Home")
|
||||
return new PageRouteModel("/Home.cshtml", "/Home")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
|
|
@ -353,28 +217,27 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
return new FileProviderRazorProjectItem(testFileInfo, basePath, path);
|
||||
}
|
||||
|
||||
private class TestPageApplicationModelProvider : IPageApplicationModelProvider
|
||||
private class TestPageRouteModelProvider : IPageRouteModelProvider
|
||||
{
|
||||
private readonly PageApplicationModel[] _models;
|
||||
private readonly PageRouteModel[] _models;
|
||||
|
||||
public TestPageApplicationModelProvider(params PageApplicationModel[] models)
|
||||
public TestPageRouteModelProvider(params PageRouteModel[] models)
|
||||
{
|
||||
_models = models ?? Array.Empty<PageApplicationModel>();
|
||||
_models = models ?? Array.Empty<PageRouteModel>();
|
||||
}
|
||||
|
||||
public int Order => -1000;
|
||||
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
public void OnProvidersExecuted(PageRouteModelProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
public void OnProvidersExecuting(PageRouteModelProviderContext context)
|
||||
{
|
||||
foreach (var model in _models)
|
||||
{
|
||||
context.Results.Add(model);
|
||||
context.RouteModels.Add(model);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
// 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 System.Reflection;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class AuthorizationPageApplicationModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_IgnoresAttributesOnHandlerMethods()
|
||||
{
|
||||
// Arrange
|
||||
var policyProvider = new DefaultAuthorizationPolicyProvider(new TestOptionsManager<AuthorizationOptions>());
|
||||
var autorizationProvider = new AuthorizationPageApplicationModelProvider(policyProvider);
|
||||
var typeInfo = typeof(PageWiithAuthorizeHandlers).GetTypeInfo();
|
||||
var context = GetApplicationProviderContext(typeInfo);
|
||||
|
||||
// Act
|
||||
autorizationProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(context.PageApplicationModel.Filters);
|
||||
}
|
||||
|
||||
private class PageWiithAuthorizeHandlers
|
||||
{
|
||||
public ModelWuthAuthorizeHandlers Model => null;
|
||||
}
|
||||
|
||||
public class ModelWuthAuthorizeHandlers
|
||||
{
|
||||
[Authorize]
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_AddsAuthorizeFilter_IfModelHasAuthorizationAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var policyProvider = new DefaultAuthorizationPolicyProvider(new TestOptionsManager<AuthorizationOptions>());
|
||||
var autorizationProvider = new AuthorizationPageApplicationModelProvider(policyProvider);
|
||||
var context = GetApplicationProviderContext(typeof(TestPage).GetTypeInfo());
|
||||
|
||||
// Act
|
||||
autorizationProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.Filters,
|
||||
f => Assert.IsType<AuthorizeFilter>(f));
|
||||
}
|
||||
|
||||
private class TestPage
|
||||
{
|
||||
public TestModel Model => null;
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
private class TestModel
|
||||
{
|
||||
public virtual void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_CollatesAttributesFromInheritedTypes()
|
||||
{
|
||||
// Arrange
|
||||
var options = new TestOptionsManager<AuthorizationOptions>();
|
||||
options.Value.AddPolicy("Base", policy => policy.RequireClaim("Basic").RequireClaim("Basic2"));
|
||||
options.Value.AddPolicy("Derived", policy => policy.RequireClaim("Derived"));
|
||||
|
||||
var policyProvider = new DefaultAuthorizationPolicyProvider(options);
|
||||
var autorizationProvider = new AuthorizationPageApplicationModelProvider(policyProvider);
|
||||
|
||||
var context = GetApplicationProviderContext(typeof(TestPageWithDerivedModel).GetTypeInfo());
|
||||
|
||||
// Act
|
||||
autorizationProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(context.PageApplicationModel.Filters));
|
||||
// Basic + Basic2 + Derived authorize
|
||||
Assert.Equal(3, authorizeFilter.Policy.Requirements.Count);
|
||||
}
|
||||
|
||||
private class TestPageWithDerivedModel
|
||||
{
|
||||
public DeriviedModel Model => null;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Base")]
|
||||
public class BaseModel
|
||||
{
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Derived")]
|
||||
private class DeriviedModel : BaseModel
|
||||
{
|
||||
public virtual void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_AddsAllowAnonymousFilter()
|
||||
{
|
||||
// Arrange
|
||||
var policyProvider = new DefaultAuthorizationPolicyProvider(new TestOptionsManager<AuthorizationOptions>());
|
||||
var autorizationProvider = new AuthorizationPageApplicationModelProvider(policyProvider);
|
||||
var context = GetApplicationProviderContext(typeof(PageWithAnonymousModel).GetTypeInfo());
|
||||
|
||||
// Act
|
||||
autorizationProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.Filters,
|
||||
f => Assert.IsType<AllowAnonymousFilter>(f));
|
||||
}
|
||||
|
||||
private class PageWithAnonymousModel
|
||||
{
|
||||
public AnonymousModel Model => null;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public class AnonymousModel
|
||||
{
|
||||
public void OnGet() { }
|
||||
}
|
||||
|
||||
private static PageApplicationModelProviderContext GetApplicationProviderContext(TypeInfo typeInfo)
|
||||
{
|
||||
var defaultProvider = new DefaultPageApplicationModelProvider(new TestOptionsManager<MvcOptions>());
|
||||
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
|
||||
defaultProvider.OnProvidersExecuting(context);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// 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.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class AutoValidateAntiforgeryPageApplicationModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_AddsFiltersToModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new PageActionDescriptor();
|
||||
var applicationModel = new PageApplicationModel(
|
||||
actionDescriptor,
|
||||
typeof(object).GetTypeInfo(),
|
||||
new object[0]);
|
||||
var applicationModelProvider = new AutoValidateAntiforgeryPageApplicationModelProvider();
|
||||
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeof(object).GetTypeInfo())
|
||||
{
|
||||
PageApplicationModel = applicationModel,
|
||||
};
|
||||
|
||||
// Act
|
||||
applicationModelProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
applicationModel.Filters,
|
||||
filter => Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
// 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 System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class CompiledPageActionDescriptorBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateDescriptor_CopiesPropertiesFromPageActionDescriptor()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new PageActionDescriptor
|
||||
{
|
||||
ActionConstraints = new List<IActionConstraintMetadata>(),
|
||||
AttributeRouteInfo = new AttributeRouteInfo(),
|
||||
FilterDescriptors = new List<FilterDescriptor>(),
|
||||
RelativePath = "/Foo",
|
||||
RouteValues = new Dictionary<string, string>(),
|
||||
ViewEnginePath = "/Pages/Foo",
|
||||
};
|
||||
var handlerTypeInfo = typeof(object).GetTypeInfo();
|
||||
var pageApplicationModel = new PageApplicationModel(actionDescriptor, handlerTypeInfo, new object[0]);
|
||||
|
||||
// Act
|
||||
var actual = CompiledPageActionDescriptorBuilder.Build(pageApplicationModel);
|
||||
|
||||
// Assert
|
||||
Assert.Same(actionDescriptor.ActionConstraints, actual.ActionConstraints);
|
||||
Assert.Same(actionDescriptor.AttributeRouteInfo, actual.AttributeRouteInfo);
|
||||
Assert.Same(actionDescriptor.RelativePath, actual.RelativePath);
|
||||
Assert.Same(actionDescriptor.RouteValues, actual.RouteValues);
|
||||
Assert.Same(actionDescriptor.ViewEnginePath, actual.ViewEnginePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptor_CopiesPropertiesFromPageApplicationModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new PageActionDescriptor
|
||||
{
|
||||
ActionConstraints = new List<IActionConstraintMetadata>(),
|
||||
AttributeRouteInfo = new AttributeRouteInfo(),
|
||||
FilterDescriptors = new List<FilterDescriptor>(),
|
||||
RelativePath = "/Foo",
|
||||
RouteValues = new Dictionary<string, string>(),
|
||||
ViewEnginePath = "/Pages/Foo",
|
||||
};
|
||||
var handlerTypeInfo = typeof(TestModel).GetTypeInfo();
|
||||
var pageApplicationModel = new PageApplicationModel(actionDescriptor, handlerTypeInfo, new object[0])
|
||||
{
|
||||
PageType = typeof(TestPage).GetTypeInfo(),
|
||||
ModelType = typeof(TestModel).GetTypeInfo(),
|
||||
Filters =
|
||||
{
|
||||
Mock.Of<IFilterMetadata>(),
|
||||
Mock.Of<IFilterMetadata>(),
|
||||
},
|
||||
HandlerMethods =
|
||||
{
|
||||
new PageHandlerModel(handlerTypeInfo.GetMethod(nameof(TestModel.OnGet)), new object[0]),
|
||||
},
|
||||
HandlerProperties =
|
||||
{
|
||||
new PagePropertyModel(handlerTypeInfo.GetProperty(nameof(TestModel.Property)), new object[0])
|
||||
{
|
||||
BindingInfo = new BindingInfo(),
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var actual = CompiledPageActionDescriptorBuilder.Build(pageApplicationModel);
|
||||
|
||||
// Assert
|
||||
Assert.Same(pageApplicationModel.PageType, actual.PageTypeInfo);
|
||||
Assert.Same(pageApplicationModel.ModelType, actual.ModelTypeInfo);
|
||||
Assert.Same(pageApplicationModel.HandlerType, actual.HandlerTypeInfo);
|
||||
Assert.Same(pageApplicationModel.Properties, actual.Properties);
|
||||
Assert.Equal(pageApplicationModel.Filters, actual.FilterDescriptors.Select(f => f.Filter));
|
||||
Assert.Equal(pageApplicationModel.HandlerMethods.Select(p => p.MethodInfo), actual.HandlerMethods.Select(p => p.MethodInfo));
|
||||
Assert.Equal(pageApplicationModel.HandlerProperties.Select(p => p.PropertyName), actual.BoundProperties.Select(p => p.Name));
|
||||
}
|
||||
|
||||
private class TestPage
|
||||
{
|
||||
public TestModel Model { get; } = new TestModel();
|
||||
|
||||
[BindProperty]
|
||||
public string Property { get; set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class TestModel
|
||||
{
|
||||
[BindProperty]
|
||||
public string Property { get; set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_CopiesPropertiesFromHandlerModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new PageActionDescriptor();
|
||||
var handlerTypeInfo = typeof(ModelWithHandler).GetTypeInfo();
|
||||
var handlerModel = new PageHandlerModel(handlerTypeInfo.GetMethod(nameof(ModelWithHandler.OnGetCustomerAsync)), new object[0])
|
||||
{
|
||||
HttpMethod = "GET",
|
||||
HandlerName = "Customer",
|
||||
};
|
||||
var pageApplicationModel = new PageApplicationModel(actionDescriptor, handlerTypeInfo, new object[0])
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
handlerModel,
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var handlerDescriptors = CompiledPageActionDescriptorBuilder.CreateHandlerMethods(pageApplicationModel);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
handlerDescriptors,
|
||||
d =>
|
||||
{
|
||||
Assert.Equal(handlerModel.MethodInfo, d.MethodInfo);
|
||||
Assert.Equal(handlerModel.HttpMethod, d.HttpMethod);
|
||||
Assert.Equal(handlerModel.HandlerName, d.Name);
|
||||
});
|
||||
}
|
||||
|
||||
private class ModelWithHandler
|
||||
{
|
||||
public void OnGetCustomerAsync()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_CopiesParameterDecriptorsFromParameterModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new PageActionDescriptor();
|
||||
var handlerTypeInfo = typeof(HandlerWithParameters).GetTypeInfo();
|
||||
var handlerMethod = handlerTypeInfo.GetMethod(nameof(HandlerWithParameters.OnPost));
|
||||
var parameters = handlerMethod.GetParameters();
|
||||
var parameterModel1 = new PageParameterModel(parameters[0], new object[0])
|
||||
{
|
||||
ParameterName = "test-name"
|
||||
};
|
||||
var parameterModel2 = new PageParameterModel(parameters[1], new object[0])
|
||||
{
|
||||
BindingInfo = new BindingInfo(),
|
||||
};
|
||||
var handlerModel = new PageHandlerModel(handlerMethod, new object[0])
|
||||
{
|
||||
Parameters =
|
||||
{
|
||||
parameterModel1,
|
||||
parameterModel2,
|
||||
}
|
||||
};
|
||||
var pageApplicationModel = new PageApplicationModel(actionDescriptor, handlerTypeInfo, new object[0])
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
handlerModel,
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var handlerDescriptors = CompiledPageActionDescriptorBuilder.CreateHandlerMethods(pageApplicationModel);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
Assert.Single(handlerDescriptors).Parameters,
|
||||
p =>
|
||||
{
|
||||
Assert.Equal(parameters[0], p.ParameterInfo);
|
||||
Assert.Equal(typeof(string), p.ParameterType);
|
||||
Assert.Equal(parameterModel1.ParameterName, p.Name);
|
||||
},
|
||||
p =>
|
||||
{
|
||||
Assert.Equal(parameters[1], p.ParameterInfo);
|
||||
Assert.Equal(typeof(int), p.ParameterType);
|
||||
Assert.Same(parameterModel2.BindingInfo, p.BindingInfo);
|
||||
});
|
||||
}
|
||||
|
||||
private class HandlerWithParameters
|
||||
{
|
||||
public void OnPost(string param1, [FromRoute(Name = "id")] int param2)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateBoundProperties_CopiesPropertyDescriptorsFromPagePropertyModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new PageActionDescriptor();
|
||||
var handlerTypeInfo = typeof(HandlerWithProperty).GetTypeInfo();
|
||||
var propertyModel = new PagePropertyModel(
|
||||
handlerTypeInfo.GetProperty(nameof(HandlerWithProperty.Property)),
|
||||
new object[0])
|
||||
{
|
||||
PropertyName = nameof(HandlerWithProperty.Property),
|
||||
BindingInfo = new BindingInfo(),
|
||||
};
|
||||
var pageApplicationModel = new PageApplicationModel(actionDescriptor, handlerTypeInfo, new object[0])
|
||||
{
|
||||
HandlerProperties =
|
||||
{
|
||||
propertyModel,
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var propertyDescriptors = CompiledPageActionDescriptorBuilder.CreateBoundProperties(pageApplicationModel);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
propertyDescriptors,
|
||||
p =>
|
||||
{
|
||||
Assert.Same(propertyModel.PropertyName, p.Name);
|
||||
Assert.Same(typeof(int), p.ParameterType);
|
||||
Assert.Same(propertyModel.PropertyInfo, p.Property);
|
||||
Assert.Same(propertyModel.BindingInfo, p.BindingInfo);
|
||||
});
|
||||
}
|
||||
|
||||
private class HandlerWithProperty
|
||||
{
|
||||
[BindProperty]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateBoundProperties_IgnoresPropertiesWithoutBindingInfo()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new PageActionDescriptor();
|
||||
var handlerTypeInfo = typeof(HandlerWithIgnoredProperties).GetTypeInfo();
|
||||
var propertyModel1 = new PagePropertyModel(
|
||||
handlerTypeInfo.GetProperty(nameof(HandlerWithIgnoredProperties.Property)),
|
||||
new object[0])
|
||||
{
|
||||
PropertyName = nameof(HandlerWithIgnoredProperties.Property),
|
||||
BindingInfo = new BindingInfo(),
|
||||
};
|
||||
var propertyModel2 = new PagePropertyModel(
|
||||
handlerTypeInfo.GetProperty(nameof(HandlerWithIgnoredProperties.IgnoreMe)),
|
||||
new object[0])
|
||||
{
|
||||
PropertyName = nameof(HandlerWithIgnoredProperties.IgnoreMe),
|
||||
};
|
||||
var pageApplicationModel = new PageApplicationModel(actionDescriptor, handlerTypeInfo, new object[0])
|
||||
{
|
||||
HandlerProperties =
|
||||
{
|
||||
propertyModel1,
|
||||
propertyModel2,
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var propertyDescriptors = CompiledPageActionDescriptorBuilder.CreateBoundProperties(pageApplicationModel);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
propertyDescriptors,
|
||||
p =>
|
||||
{
|
||||
Assert.Same(propertyModel1.PropertyName, p.Name);
|
||||
Assert.Same(typeof(int), p.ParameterType);
|
||||
Assert.Same(propertyModel1.PropertyInfo, p.Property);
|
||||
Assert.Same(propertyModel1.BindingInfo, p.BindingInfo);
|
||||
});
|
||||
}
|
||||
|
||||
private class HandlerWithIgnoredProperties
|
||||
{
|
||||
[BindProperty]
|
||||
public int Property { get; set; }
|
||||
|
||||
public string IgnoreMe { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class CompiledPageApplicationModelProviderTest
|
||||
public class CompiledPageRouteModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_AddsModelsForCompiledViews()
|
||||
|
|
@ -22,14 +22,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
GetDescriptor("/Pages/About.cshtml"),
|
||||
GetDescriptor("/Pages/Home.cshtml", "some-prefix"),
|
||||
};
|
||||
var provider = new TestCompiledPageApplicationModelProvider(descriptors, new RazorPagesOptions());
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new TestCompiledPageRouteModelProvider(descriptors, new RazorPagesOptions());
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.Results,
|
||||
Assert.Collection(context.RouteModels,
|
||||
result =>
|
||||
{
|
||||
Assert.Equal("/Pages/About.cshtml", result.RelativePath);
|
||||
|
|
@ -55,14 +55,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
GetDescriptor("/Pages/Index.cshtml"),
|
||||
GetDescriptor("/Pages/Admin/Index.cshtml", "some-template"),
|
||||
};
|
||||
var provider = new TestCompiledPageApplicationModelProvider(descriptors, new RazorPagesOptions { RootDirectory = "/" });
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new TestCompiledPageRouteModelProvider(descriptors, new RazorPagesOptions { RootDirectory = "/" });
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.Results,
|
||||
Assert.Collection(context.RouteModels,
|
||||
result =>
|
||||
{
|
||||
Assert.Equal("/Pages/Index.cshtml", result.RelativePath);
|
||||
|
|
@ -90,14 +90,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
GetDescriptor("/Pages/Index.cshtml"),
|
||||
GetDescriptor("/Pages/Admin/Index.cshtml", "some-template"),
|
||||
};
|
||||
var provider = new TestCompiledPageApplicationModelProvider(descriptors, new RazorPagesOptions());
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new TestCompiledPageRouteModelProvider(descriptors, new RazorPagesOptions());
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.Results,
|
||||
Assert.Collection(context.RouteModels,
|
||||
result =>
|
||||
{
|
||||
Assert.Equal("/Pages/Index.cshtml", result.RelativePath);
|
||||
|
|
@ -125,8 +125,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
GetDescriptor("/Pages/Index.cshtml"),
|
||||
GetDescriptor("/Pages/Home.cshtml", "/some-prefix"),
|
||||
};
|
||||
var provider = new TestCompiledPageApplicationModelProvider(descriptors, new RazorPagesOptions());
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new TestCompiledPageRouteModelProvider(descriptors, new RazorPagesOptions());
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
|
||||
|
|
@ -143,11 +143,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
};
|
||||
}
|
||||
|
||||
public class TestCompiledPageApplicationModelProvider : CompiledPageApplicationModelProvider
|
||||
public class TestCompiledPageRouteModelProvider : CompiledPageRouteModelProvider
|
||||
{
|
||||
private readonly IEnumerable<CompiledViewDescriptor> _descriptors;
|
||||
|
||||
public TestCompiledPageApplicationModelProvider(IEnumerable<CompiledViewDescriptor> descriptors, RazorPagesOptions options)
|
||||
public TestCompiledPageRouteModelProvider(IEnumerable<CompiledViewDescriptor> descriptors, RazorPagesOptions options)
|
||||
: base(new ApplicationPartManager(), new TestOptionsManager<RazorPagesOptions>(options))
|
||||
{
|
||||
_descriptors = descriptors;
|
||||
|
|
@ -0,0 +1,788 @@
|
|||
// 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.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public partial class DefaultPageApplicationModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_SetsPageAsHandlerType_IfModelPropertyDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(TestPage).GetTypeInfo();
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Same(context.PageApplicationModel.PageType, context.PageApplicationModel.HandlerType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_SetsPageAsHandlerType_IfModelTypeDoesNotHaveAnyHandlers()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithModelWithoutHandlers).GetTypeInfo();
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Same(context.PageApplicationModel.PageType, context.PageApplicationModel.HandlerType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_SetsModelAsHandlerType()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithModel).GetTypeInfo();
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Same(typeof(TestPageModel).GetTypeInfo(), context.PageApplicationModel.HandlerType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DiscoversPropertiesFromPage()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(TestPage).GetTypeInfo();
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.HandlerProperties.OrderBy(p => p.PropertyName),
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(typeInfo.GetProperty(nameof(TestPage.Property1)), property.PropertyInfo);
|
||||
Assert.Null(property.BindingInfo);
|
||||
Assert.Equal(nameof(TestPage.Property1), property.PropertyName);
|
||||
},
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(typeInfo.GetProperty(nameof(TestPage.Property2)), property.PropertyInfo);
|
||||
Assert.Equal(nameof(TestPage.Property2), property.PropertyName);
|
||||
Assert.NotNull(property.BindingInfo);
|
||||
Assert.Equal(BindingSource.Path, property.BindingInfo.BindingSource);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DiscoversHandlersFromPage()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithModelWithoutHandlers).GetTypeInfo();
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.HandlerMethods.OrderBy(p => p.Name),
|
||||
handler =>
|
||||
{
|
||||
var name = nameof(PageWithModelWithoutHandlers.OnGet);
|
||||
Assert.Equal(typeInfo.GetMethod(name), handler.MethodInfo);
|
||||
Assert.Equal(name, handler.Name);
|
||||
Assert.Equal("Get", handler.HttpMethod);
|
||||
Assert.Null(handler.HandlerName);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
var name = nameof(PageWithModelWithoutHandlers.OnPostAsync);
|
||||
Assert.Equal(typeInfo.GetMethod(name), handler.MethodInfo);
|
||||
Assert.Equal(name, handler.Name);
|
||||
Assert.Equal("Post", handler.HttpMethod);
|
||||
Assert.Null(handler.HandlerName);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
var name = nameof(PageWithModelWithoutHandlers.OnPostDeleteCustomerAsync);
|
||||
Assert.Equal(typeInfo.GetMethod(name), handler.MethodInfo);
|
||||
Assert.Equal(name, handler.Name);
|
||||
Assert.Equal("Post", handler.HttpMethod);
|
||||
Assert.Equal("DeleteCustomer", handler.HandlerName);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DiscoversPropertiesFromModel()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithModel).GetTypeInfo();
|
||||
var modelType = typeof(TestPageModel);
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.HandlerProperties.OrderBy(p => p.PropertyName),
|
||||
property =>
|
||||
{
|
||||
var name = nameof(TestPageModel.Property1);
|
||||
Assert.Equal(modelType.GetProperty(name), property.PropertyInfo);
|
||||
Assert.Null(property.BindingInfo);
|
||||
Assert.Equal(name, property.PropertyName);
|
||||
},
|
||||
property =>
|
||||
{
|
||||
var name = nameof(TestPageModel.Property2);
|
||||
Assert.Equal(modelType.GetProperty(name), property.PropertyInfo);
|
||||
Assert.Equal(name, property.PropertyName);
|
||||
Assert.NotNull(property.BindingInfo);
|
||||
Assert.Equal(BindingSource.Query, property.BindingInfo.BindingSource);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DiscoversHandlersFromModel()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithModel).GetTypeInfo();
|
||||
var modelType = typeof(TestPageModel);
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.HandlerMethods.OrderBy(p => p.Name),
|
||||
handler =>
|
||||
{
|
||||
var name = nameof(TestPageModel.OnGetUser);
|
||||
Assert.Equal(modelType.GetMethod(name), handler.MethodInfo);
|
||||
Assert.Equal(name, handler.Name);
|
||||
Assert.Equal("Get", handler.HttpMethod);
|
||||
Assert.Equal("User", handler.HandlerName);
|
||||
});
|
||||
}
|
||||
|
||||
// We want to test the the 'empty' page has no bound properties, and no handler methods.
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_EmptyPage()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(EmptyPage).GetTypeInfo();
|
||||
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var pageModel = context.PageApplicationModel;
|
||||
Assert.Empty(pageModel.HandlerProperties.Where(p => p.BindingInfo != null));
|
||||
Assert.Empty(pageModel.HandlerMethods);
|
||||
Assert.Same(typeof(EmptyPage).GetTypeInfo(), pageModel.HandlerType);
|
||||
Assert.Same(typeof(EmptyPage).GetTypeInfo(), pageModel.ModelType);
|
||||
Assert.Same(typeof(EmptyPage).GetTypeInfo(), pageModel.PageType);
|
||||
}
|
||||
|
||||
// We want to test the the 'empty' page and pagemodel has no bound properties, and no handler methods.
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_EmptyPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(EmptyPageWithPageModel).GetTypeInfo();
|
||||
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var pageModel = context.PageApplicationModel;
|
||||
Assert.Empty(pageModel.HandlerProperties.Where(p => p.BindingInfo != null));
|
||||
Assert.Empty(pageModel.HandlerMethods);
|
||||
Assert.Same(typeof(EmptyPageWithPageModel).GetTypeInfo(), pageModel.HandlerType);
|
||||
Assert.Same(typeof(EmptyPageModel).GetTypeInfo(), pageModel.ModelType);
|
||||
Assert.Same(typeof(EmptyPageWithPageModel).GetTypeInfo(), pageModel.PageType);
|
||||
}
|
||||
|
||||
private class EmptyPage : Page
|
||||
{
|
||||
// Copied from generated code
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<EmptyPage> Html { get; private set; }
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<EmptyPage> ViewData => null;
|
||||
public EmptyPage Model => ViewData.Model;
|
||||
|
||||
public override Task ExecuteAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyPageWithPageModel : Page
|
||||
{
|
||||
// Copied from generated code
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<EmptyPageModel> Html { get; private set; }
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<EmptyPageModel> ViewData => null;
|
||||
public EmptyPageModel Model => ViewData.Model;
|
||||
|
||||
public override Task ExecuteAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyPageModel : PageModel
|
||||
{
|
||||
}
|
||||
|
||||
[Fact] // If the model has handler methods, we prefer those.
|
||||
public void CreateDescriptor_FindsHandlerMethod_OnModel()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithHandlerThatGetsIgnored).GetTypeInfo();
|
||||
var modelType = typeof(ModelWithHandler);
|
||||
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var pageModel = context.PageApplicationModel;
|
||||
Assert.Collection(
|
||||
pageModel.HandlerProperties,
|
||||
p => Assert.Equal(modelType.GetProperty(nameof(ModelWithHandler.BindMe)), p.PropertyInfo));
|
||||
|
||||
Assert.Collection(
|
||||
pageModel.HandlerMethods,
|
||||
p => Assert.Equal(modelType.GetMethod(nameof(ModelWithHandler.OnGet)), p.MethodInfo));
|
||||
|
||||
Assert.Same(typeof(ModelWithHandler).GetTypeInfo(), pageModel.HandlerType);
|
||||
Assert.Same(typeof(ModelWithHandler).GetTypeInfo(), pageModel.ModelType);
|
||||
Assert.Same(typeof(PageWithHandlerThatGetsIgnored).GetTypeInfo(), pageModel.PageType);
|
||||
}
|
||||
|
||||
private class ModelWithHandler
|
||||
{
|
||||
[ModelBinder]
|
||||
public int BindMe { get; set; }
|
||||
|
||||
public void OnGet() { }
|
||||
}
|
||||
|
||||
private class PageWithHandlerThatGetsIgnored
|
||||
{
|
||||
public ModelWithHandler Model => null;
|
||||
|
||||
[ModelBinder]
|
||||
public int IgnoreMe { get; set; }
|
||||
|
||||
public void OnPost() { }
|
||||
}
|
||||
|
||||
|
||||
[Fact] // If the model has no handler methods, we look at the page instead.
|
||||
public void OnProvidersExecuting_FindsHandlerMethodOnPage_WhenModelHasNoHandlers()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithHandler).GetTypeInfo();
|
||||
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var pageModel = context.PageApplicationModel;
|
||||
Assert.Collection(
|
||||
pageModel.HandlerProperties.OrderBy(p => p.PropertyName),
|
||||
p => Assert.Equal(typeInfo.GetProperty(nameof(PageWithHandler.BindMe)), p.PropertyInfo),
|
||||
p => Assert.Equal(typeInfo.GetProperty(nameof(PageWithHandler.Model)), p.PropertyInfo));
|
||||
|
||||
Assert.Collection(
|
||||
pageModel.HandlerMethods,
|
||||
p => Assert.Equal(typeInfo.GetMethod(nameof(PageWithHandler.OnGet)), p.MethodInfo));
|
||||
|
||||
Assert.Same(typeof(PageWithHandler).GetTypeInfo(), pageModel.HandlerType);
|
||||
Assert.Same(typeof(PocoModel).GetTypeInfo(), pageModel.ModelType);
|
||||
Assert.Same(typeof(PageWithHandler).GetTypeInfo(), pageModel.PageType);
|
||||
}
|
||||
|
||||
private class PageWithHandler
|
||||
{
|
||||
public PocoModel Model => null;
|
||||
|
||||
[ModelBinder]
|
||||
public int BindMe { get; set; }
|
||||
|
||||
public void OnGet() { }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerModels_DiscoversHandlersFromBaseType()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(InheritsMethods).GetTypeInfo();
|
||||
var baseType = typeof(TestSetPageModel);
|
||||
|
||||
// Act
|
||||
var handlerModels = provider.CreateHandlerModels(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
handlerModels.OrderBy(h => h.MethodInfo.DeclaringType.Name).ThenBy(h => h.MethodInfo.Name),
|
||||
handler =>
|
||||
{
|
||||
Assert.Equal(nameof(InheritsMethods.OnGet), handler.MethodInfo.Name);
|
||||
Assert.Equal(typeInfo, handler.MethodInfo.DeclaringType.GetTypeInfo());
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
Assert.Equal(nameof(TestSetPageModel.OnGet), handler.MethodInfo.Name);
|
||||
Assert.Equal(baseType, handler.MethodInfo.DeclaringType);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
Assert.Equal(nameof(TestSetPageModel.OnPost), handler.MethodInfo.Name);
|
||||
Assert.Equal(baseType, handler.MethodInfo.DeclaringType);
|
||||
});
|
||||
}
|
||||
|
||||
private class TestSetPageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPost()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class InheritsMethods : TestSetPageModel
|
||||
{
|
||||
public new void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerModels_IgnoresNonPublicMethods()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(ProtectedModel).GetTypeInfo();
|
||||
var baseType = typeof(TestSetPageModel);
|
||||
|
||||
// Act
|
||||
var handlerModels = provider.CreateHandlerModels(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(handlerModels);
|
||||
}
|
||||
|
||||
private class ProtectedModel
|
||||
{
|
||||
protected void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnPost()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerModels_IgnoreGenericTypeParameters()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(GenericClassModel).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var handlerModels = provider.CreateHandlerModels(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(handlerModels);
|
||||
}
|
||||
|
||||
private class GenericClassModel
|
||||
{
|
||||
public void OnGet<T>()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerModels_IgnoresStaticMethods()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageModelWithStaticHandler).GetTypeInfo();
|
||||
var expected = typeInfo.GetMethod(nameof(PageModelWithStaticHandler.OnGet), BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// Act
|
||||
var handlerModels = provider.CreateHandlerModels(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
handlerModels,
|
||||
handler => Assert.Same(expected, handler.MethodInfo));
|
||||
}
|
||||
|
||||
private class PageModelWithStaticHandler
|
||||
{
|
||||
public static void OnGet(string name)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerModels_IgnoresAbstractMethods()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageModelWithAbstractMethod).GetTypeInfo();
|
||||
var expected = typeInfo.GetMethod(nameof(PageModelWithAbstractMethod.OnGet), BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// Act
|
||||
var handlerModels = provider.CreateHandlerModels(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
handlerModels,
|
||||
handler => Assert.Same(expected, handler.MethodInfo));
|
||||
}
|
||||
|
||||
private abstract class PageModelWithAbstractMethod
|
||||
{
|
||||
public abstract void OnPost(string name);
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerModels_IgnoresMethodWithNonHandlerAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithNonHandlerMethod).GetTypeInfo();
|
||||
var expected = typeInfo.GetMethod(nameof(PageWithNonHandlerMethod.OnGet), BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// Act
|
||||
var handlerModels = provider.CreateHandlerModels(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
handlerModels,
|
||||
handler => Assert.Same(expected, handler.MethodInfo));
|
||||
}
|
||||
|
||||
private class PageWithNonHandlerMethod
|
||||
{
|
||||
[NonHandler]
|
||||
public void OnPost(string name) { }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// There are more tests for the parsing elsewhere, this is just testing that it's wired
|
||||
// up to the model.
|
||||
[Fact]
|
||||
public void CreateHandlerModel_ParsesMethod()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageModelWithHandlerNames).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var handlerModels = provider.CreateHandlerModels(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
handlerModels.OrderBy(h => h.MethodInfo.Name),
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(typeInfo.GetMethod(nameof(PageModelWithHandlerNames.OnPutDeleteAsync)), handler.MethodInfo);
|
||||
Assert.Equal("Put", handler.HttpMethod);
|
||||
Assert.Equal("Delete", handler.HandlerName);
|
||||
});
|
||||
}
|
||||
|
||||
private class PageModelWithHandlerNames
|
||||
{
|
||||
public void OnPutDeleteAsync()
|
||||
{
|
||||
}
|
||||
|
||||
public void Foo() // This isn't a valid handler name.
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_AddsParameterDescriptors()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithHandlerParameters).GetTypeInfo();
|
||||
var expected = typeInfo.GetMethod(nameof(PageWithHandlerParameters.OnPost));
|
||||
|
||||
// Act
|
||||
var handlerModels = provider.CreateHandlerModels(typeInfo);
|
||||
|
||||
// Assert
|
||||
var handler = Assert.Single(handlerModels);
|
||||
|
||||
Assert.Collection(
|
||||
handler.Parameters,
|
||||
p =>
|
||||
{
|
||||
Assert.NotNull(p.ParameterInfo);
|
||||
Assert.Equal(typeof(string), p.ParameterInfo.ParameterType);
|
||||
Assert.Equal("name", p.ParameterName);
|
||||
},
|
||||
p =>
|
||||
{
|
||||
Assert.NotNull(p.ParameterInfo);
|
||||
Assert.Equal(typeof(int), p.ParameterInfo.ParameterType);
|
||||
Assert.Equal("id", p.ParameterName);
|
||||
Assert.Equal("personId", p.BindingInfo.BinderModelName);
|
||||
});
|
||||
}
|
||||
|
||||
private class PageWithHandlerParameters
|
||||
{
|
||||
public void OnPost(string name, [ModelBinder(Name = "personId")] int id) { }
|
||||
}
|
||||
|
||||
// We're using PropertyHelper from Common to find the properties here, which implements
|
||||
// out standard set of semantics for properties that the framework interacts with.
|
||||
//
|
||||
// One of the desirable consequences of that is we only find 'visible' properties. We're not
|
||||
// retesting all of the details of PropertyHelper here, just the visibility part as a quick check
|
||||
// that we're using PropertyHelper as expected.
|
||||
[Fact]
|
||||
public void PopulateHandlerProperties_UsesPropertyHelpers_ToFindProperties()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(HidesAProperty).GetTypeInfo();
|
||||
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
|
||||
|
||||
// Act
|
||||
provider.PopulateHandlerProperties(pageModel);
|
||||
|
||||
// Assert
|
||||
var properties = pageModel.HandlerProperties;
|
||||
Assert.Collection(
|
||||
properties,
|
||||
p =>
|
||||
{
|
||||
Assert.Equal(typeof(HidesAProperty).GetTypeInfo(), p.PropertyInfo.DeclaringType.GetTypeInfo());
|
||||
});
|
||||
}
|
||||
|
||||
private class HasAHiddenProperty
|
||||
{
|
||||
[BindProperty]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
private class HidesAProperty : HasAHiddenProperty
|
||||
{
|
||||
[BindProperty]
|
||||
public new int Property { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerProperties_SupportsGet_OnProperty()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestPageApplicationModelProvider();
|
||||
var typeInfo = typeof(ModelSupportsGetOnProperty).GetTypeInfo();
|
||||
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
|
||||
|
||||
// Act
|
||||
provider.PopulateHandlerProperties(pageModel);
|
||||
|
||||
// Assert
|
||||
var properties = pageModel.HandlerProperties;
|
||||
Assert.Collection(
|
||||
properties.OrderBy(p => p.PropertyName),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal(typeInfo.GetProperty(nameof(ModelSupportsGetOnProperty.Property)), p.PropertyInfo);
|
||||
Assert.NotNull(p.BindingInfo.RequestPredicate);
|
||||
Assert.True(p.BindingInfo.RequestPredicate(new ActionContext
|
||||
{
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method ="GET",
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private class ModelSupportsGetOnProperty
|
||||
{
|
||||
[BindProperty(SupportsGet = true)]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Foo")]
|
||||
[InlineData("On")]
|
||||
[InlineData("OnAsync")]
|
||||
[InlineData("Async")]
|
||||
public void TryParseHandler_ParsesHandlerNames_InvalidData(string methodName)
|
||||
{
|
||||
// Act
|
||||
var result = DefaultPageApplicationModelProvider.TryParseHandlerMethod(methodName, out var httpMethod, out var handler);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(httpMethod);
|
||||
Assert.Null(handler);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("OnG", "G", null)]
|
||||
[InlineData("OnGAsync", "G", null)]
|
||||
[InlineData("OnPOST", "P", "OST")]
|
||||
[InlineData("OnPOSTAsync", "P", "OST")]
|
||||
[InlineData("OnDeleteFoo", "Delete", "Foo")]
|
||||
[InlineData("OnDeleteFooAsync", "Delete", "Foo")]
|
||||
[InlineData("OnMadeupLongHandlerName", "Madeup", "LongHandlerName")]
|
||||
[InlineData("OnMadeupLongHandlerNameAsync", "Madeup", "LongHandlerName")]
|
||||
public void TryParseHandler_ParsesHandlerNames_ValidData(string methodName, string expectedHttpMethod, string expectedHandler)
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = DefaultPageApplicationModelProvider.TryParseHandlerMethod(methodName, out var httpMethod, out var handler);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedHttpMethod, httpMethod);
|
||||
Assert.Equal(expectedHandler, handler);
|
||||
}
|
||||
|
||||
private class TestPageApplicationModelProvider : DefaultPageApplicationModelProvider
|
||||
{
|
||||
public TestPageApplicationModelProvider(IOptions<MvcOptions> mvcOptions = null)
|
||||
: base(mvcOptions : new TestOptionsManager<MvcOptions>())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class TestPage
|
||||
{
|
||||
public string Property1 { get; set; }
|
||||
|
||||
[FromRoute]
|
||||
public object Property2 { get; set; }
|
||||
}
|
||||
|
||||
private class PageWithModelWithoutHandlers : Page
|
||||
{
|
||||
public ModelWithoutHandler Model { get; }
|
||||
|
||||
public override Task ExecuteAsync() => throw new NotImplementedException();
|
||||
|
||||
public void OnGet() { }
|
||||
|
||||
public void OnPostAsync() { }
|
||||
|
||||
public void OnPostDeleteCustomerAsync() { }
|
||||
|
||||
public class ModelWithoutHandler
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class PageWithModel : Page
|
||||
{
|
||||
public TestPageModel Model { get; }
|
||||
|
||||
public override Task ExecuteAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class TestPageModel
|
||||
{
|
||||
public string Property1 { get; set; }
|
||||
|
||||
[FromQuery]
|
||||
public string Property2 { get; set; }
|
||||
|
||||
public void OnGetUser() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,11 @@
|
|||
// 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.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
|
|
@ -18,600 +13,126 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
public class DefaultPageLoaderTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateDescriptor_CopiesPropertiesFromBaseClass()
|
||||
public void Load_InvokesApplicationModelProviders()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new PageActionDescriptor() // We only copy the properties that are meaningful for pages.
|
||||
var descriptor = new PageActionDescriptor();
|
||||
|
||||
var compilerProvider = GetCompilerProvider();
|
||||
|
||||
var options = new TestOptionsManager<RazorPagesOptions>();
|
||||
|
||||
var provider1 = new Mock<IPageApplicationModelProvider>();
|
||||
var provider2 = new Mock<IPageApplicationModelProvider>();
|
||||
|
||||
var sequence = 0;
|
||||
var pageApplicationModel1 = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
var pageApplicationModel2 = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
|
||||
provider1.Setup(p => p.OnProvidersExecuting(It.IsAny<PageApplicationModelProviderContext>()))
|
||||
.Callback((PageApplicationModelProviderContext c) =>
|
||||
{
|
||||
Assert.Equal(0, sequence++);
|
||||
Assert.Null(c.PageApplicationModel);
|
||||
c.PageApplicationModel = pageApplicationModel1;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
provider2.Setup(p => p.OnProvidersExecuting(It.IsAny<PageApplicationModelProviderContext>()))
|
||||
.Callback((PageApplicationModelProviderContext c) =>
|
||||
{
|
||||
Assert.Equal(1, sequence++);
|
||||
Assert.Same(pageApplicationModel1, c.PageApplicationModel);
|
||||
c.PageApplicationModel = pageApplicationModel2;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
provider1.Setup(p => p.OnProvidersExecuted(It.IsAny<PageApplicationModelProviderContext>()))
|
||||
.Callback((PageApplicationModelProviderContext c) =>
|
||||
{
|
||||
Assert.Equal(3, sequence++);
|
||||
Assert.Same(pageApplicationModel2, c.PageApplicationModel);
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
provider2.Setup(p => p.OnProvidersExecuted(It.IsAny<PageApplicationModelProviderContext>()))
|
||||
.Callback((PageApplicationModelProviderContext c) =>
|
||||
{
|
||||
Assert.Equal(2, sequence++);
|
||||
Assert.Same(pageApplicationModel2, c.PageApplicationModel);
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var providers = new[]
|
||||
{
|
||||
ActionConstraints = new List<IActionConstraintMetadata>(),
|
||||
AttributeRouteInfo = new AttributeRouteInfo(),
|
||||
FilterDescriptors = new List<FilterDescriptor>(),
|
||||
RelativePath = "/Foo",
|
||||
RouteValues = new Dictionary<string, string>(),
|
||||
ViewEnginePath = "/Pages/Foo",
|
||||
provider1.Object, provider2.Object
|
||||
};
|
||||
|
||||
// Act
|
||||
var actual = DefaultPageLoader.CreateDescriptor(expected,
|
||||
new RazorPageAttribute(expected.RelativePath, typeof(EmptyPage), ""));
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected.ActionConstraints, actual.ActionConstraints);
|
||||
Assert.Same(expected.AttributeRouteInfo, actual.AttributeRouteInfo);
|
||||
Assert.Same(expected.FilterDescriptors, actual.FilterDescriptors);
|
||||
Assert.Same(expected.Properties, actual.Properties);
|
||||
Assert.Same(expected.RelativePath, actual.RelativePath);
|
||||
Assert.Same(expected.RouteValues, actual.RouteValues);
|
||||
Assert.Same(expected.ViewEnginePath, actual.ViewEnginePath);
|
||||
}
|
||||
|
||||
// We want to test the the 'empty' page has no bound properties, and no handler methods.
|
||||
[Fact]
|
||||
public void CreateDescriptor_EmptyPage()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(EmptyPage);
|
||||
var loader = new DefaultPageLoader(
|
||||
providers,
|
||||
compilerProvider,
|
||||
options);
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.CreateDescriptor(new PageActionDescriptor(),
|
||||
new RazorPageAttribute("/Pages/Index", type, ""));
|
||||
var result = loader.Load(new PageActionDescriptor());
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.BoundProperties);
|
||||
Assert.Empty(result.HandlerMethods);
|
||||
Assert.Same(typeof(EmptyPage).GetTypeInfo(), result.HandlerTypeInfo);
|
||||
Assert.Same(typeof(EmptyPage).GetTypeInfo(), result.ModelTypeInfo);
|
||||
Assert.Same(typeof(EmptyPage).GetTypeInfo(), result.PageTypeInfo);
|
||||
}
|
||||
|
||||
// We want to test the the 'empty' page and pagemodel has no bound properties, and no handler methods.
|
||||
[Fact]
|
||||
public void CreateDescriptor_EmptyPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(EmptyPageWithPageModel);
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.CreateDescriptor(new PageActionDescriptor(),
|
||||
new RazorPageAttribute("/Pages/Index", type, ""));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.BoundProperties);
|
||||
Assert.Empty(result.HandlerMethods);
|
||||
Assert.Same(typeof(EmptyPageWithPageModel).GetTypeInfo(), result.HandlerTypeInfo);
|
||||
Assert.Same(typeof(EmptyPageModel).GetTypeInfo(), result.ModelTypeInfo);
|
||||
Assert.Same(typeof(EmptyPageWithPageModel).GetTypeInfo(), result.PageTypeInfo);
|
||||
}
|
||||
|
||||
private class EmptyPage : Page
|
||||
{
|
||||
// Copied from generated code
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<EmptyPage> Html { get; private set; }
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<EmptyPage> ViewData => null;
|
||||
public EmptyPage Model => ViewData.Model;
|
||||
|
||||
public override Task ExecuteAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyPageWithPageModel : Page
|
||||
{
|
||||
// Copied from generated code
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<EmptyPageModel> Html { get; private set; }
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<EmptyPageModel> ViewData => null;
|
||||
public EmptyPageModel Model => ViewData.Model;
|
||||
|
||||
public override Task ExecuteAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyPageModel : PageModel
|
||||
{
|
||||
}
|
||||
|
||||
[Fact] // If the model has handler methods, we prefer those.
|
||||
public void CreateDescriptor_FindsHandlerMethod_OnModel()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageWithHandlerThatGetsIgnored);
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.CreateDescriptor(new PageActionDescriptor(),
|
||||
new RazorPageAttribute("/Pages/Index", type, ""));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.BoundProperties, p => Assert.Equal("BindMe", p.Name));
|
||||
Assert.Collection(result.HandlerMethods, h => Assert.Equal("OnGet", h.MethodInfo.Name));
|
||||
Assert.Same(typeof(ModelWithHandler).GetTypeInfo(), result.HandlerTypeInfo);
|
||||
Assert.Same(typeof(ModelWithHandler).GetTypeInfo(), result.ModelTypeInfo);
|
||||
Assert.Same(typeof(PageWithHandlerThatGetsIgnored).GetTypeInfo(), result.PageTypeInfo);
|
||||
}
|
||||
|
||||
private class ModelWithHandler
|
||||
{
|
||||
[ModelBinder]
|
||||
public int BindMe { get; set; }
|
||||
|
||||
public void OnGet() { }
|
||||
}
|
||||
|
||||
private class PageWithHandlerThatGetsIgnored
|
||||
{
|
||||
public ModelWithHandler Model => null;
|
||||
|
||||
[ModelBinder]
|
||||
public int IgnoreMe { get; set; }
|
||||
|
||||
public void OnPost() { }
|
||||
}
|
||||
|
||||
|
||||
[Fact] // If the model has no handler methods, we look at the page instead.
|
||||
public void CreateDescriptor_FindsHandlerMethodOnPage_WhenModelHasNoHandlers()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageWithHandler);
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.CreateDescriptor(new PageActionDescriptor(),
|
||||
new RazorPageAttribute("/Pages/Index", type, ""));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.BoundProperties, p => Assert.Equal("BindMe", p.Name));
|
||||
Assert.Collection(result.HandlerMethods, h => Assert.Equal("OnGet", h.MethodInfo.Name));
|
||||
Assert.Same(typeof(PageWithHandler).GetTypeInfo(), result.HandlerTypeInfo);
|
||||
Assert.Same(typeof(PocoModel).GetTypeInfo(), result.ModelTypeInfo);
|
||||
Assert.Same(typeof(PageWithHandler).GetTypeInfo(), result.PageTypeInfo);
|
||||
}
|
||||
|
||||
private class PocoModel
|
||||
{
|
||||
// Just a plain ol' model, nothing to see here.
|
||||
|
||||
[ModelBinder]
|
||||
public int IgnoreMe { get; set; }
|
||||
}
|
||||
|
||||
private class PageWithHandler
|
||||
{
|
||||
public PocoModel Model => null;
|
||||
|
||||
[ModelBinder]
|
||||
public int BindMe { get; set; }
|
||||
|
||||
public void OnGet() { }
|
||||
provider1.Verify();
|
||||
provider2.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_DiscoversHandlersFromBaseType()
|
||||
public void Load_InvokesApplicationModelConventions()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(InheritsMethods).GetTypeInfo();
|
||||
var descriptor = new PageActionDescriptor();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(h => h.MethodInfo.Name).ToArray(),
|
||||
(handler) =>
|
||||
var compilerProvider = GetCompilerProvider();
|
||||
|
||||
var model = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
var provider = new Mock<IPageApplicationModelProvider>();
|
||||
provider.Setup(p => p.OnProvidersExecuting(It.IsAny<PageApplicationModelProviderContext>()))
|
||||
.Callback((PageApplicationModelProviderContext c) =>
|
||||
{
|
||||
Assert.Equal("OnGet", handler.MethodInfo.Name);
|
||||
Assert.Equal(typeof(InheritsMethods), handler.MethodInfo.DeclaringType);
|
||||
},
|
||||
(handler) =>
|
||||
{
|
||||
Assert.Equal("OnGet", handler.MethodInfo.Name);
|
||||
Assert.Equal(typeof(TestSetPageModel), handler.MethodInfo.DeclaringType);
|
||||
},
|
||||
(handler) =>
|
||||
{
|
||||
Assert.Equal("OnPost", handler.MethodInfo.Name);
|
||||
Assert.Equal(typeof(TestSetPageModel), handler.MethodInfo.DeclaringType);
|
||||
c.PageApplicationModel = model;
|
||||
});
|
||||
}
|
||||
var providers = new[] { provider.Object };
|
||||
|
||||
private class TestSetPageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPost()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSetPageWithModel
|
||||
{
|
||||
public TestSetPageModel Model { get; set; }
|
||||
}
|
||||
|
||||
private class InheritsMethods : TestSetPageModel
|
||||
{
|
||||
public new void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_IgnoresNonPublicMethods()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ProtectedModel).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(results);
|
||||
}
|
||||
|
||||
private class ProtectedModel
|
||||
{
|
||||
protected void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnPost()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_IgnoreGenericTypeParameters()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(GenericClassModel).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(results);
|
||||
}
|
||||
|
||||
private class GenericClassModel
|
||||
{
|
||||
public void OnGet<T>()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_IgnoresStaticMethods()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageModelWithStaticHandler).GetTypeInfo();
|
||||
var expected = type.GetMethod(nameof(PageModelWithStaticHandler.OnGet), BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results,
|
||||
handler => Assert.Same(expected, handler.MethodInfo));
|
||||
}
|
||||
|
||||
private class PageModelWithStaticHandler
|
||||
{
|
||||
public static void OnGet(string name)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_IgnoresAbstractMethods()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageModelWithAbstractMethod).GetTypeInfo();
|
||||
var expected = type.GetMethod(nameof(PageModelWithAbstractMethod.OnGet), BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results,
|
||||
handler => Assert.Same(expected, handler.MethodInfo));
|
||||
}
|
||||
|
||||
private abstract class PageModelWithAbstractMethod
|
||||
{
|
||||
public abstract void OnPost(string name);
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_IgnoresMethodWithNonHandlerAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageWithNonHandlerMethod).GetTypeInfo();
|
||||
var expected = type.GetMethod(nameof(PageWithNonHandlerMethod.OnGet), BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results,
|
||||
handler => Assert.Same(expected, handler.MethodInfo));
|
||||
}
|
||||
|
||||
private class PageWithNonHandlerMethod
|
||||
{
|
||||
[NonHandler]
|
||||
public void OnPost(string name) { }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// There are more tests for the parsing elsewhere, this is just testing that it's wired
|
||||
// up to the descriptor.
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_ParsesMethod()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageModelWithHandlerNames).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(h => h.MethodInfo.Name),
|
||||
handler =>
|
||||
var options = new TestOptionsManager<RazorPagesOptions>();
|
||||
var convention = new Mock<IPageApplicationModelConvention>();
|
||||
convention.Setup(c => c.Apply(It.IsAny<PageApplicationModel>()))
|
||||
.Callback((PageApplicationModel m) =>
|
||||
{
|
||||
Assert.Same(type.GetMethod(nameof(PageModelWithHandlerNames.OnPutDeleteAsync)), handler.MethodInfo);
|
||||
Assert.Equal("Put", handler.HttpMethod);
|
||||
Assert.Equal("Delete", handler.Name.ToString());
|
||||
Assert.Same(model, m);
|
||||
});
|
||||
options.Value.ApplicationModelConventions.Add(convention.Object);
|
||||
|
||||
var loader = new DefaultPageLoader(
|
||||
providers,
|
||||
compilerProvider,
|
||||
options);
|
||||
|
||||
// Act
|
||||
var result = loader.Load(new PageActionDescriptor());
|
||||
|
||||
// Assert
|
||||
convention.Verify();
|
||||
}
|
||||
|
||||
private class PageModelWithHandlerNames
|
||||
private static IViewCompilerProvider GetCompilerProvider()
|
||||
{
|
||||
public void OnPutDeleteAsync()
|
||||
var descriptor = new CompiledViewDescriptor
|
||||
{
|
||||
}
|
||||
ViewAttribute = new RazorPageAttribute("/Views/Index.cshtml", typeof(object), null),
|
||||
};
|
||||
|
||||
public void Foo() // This isn't a valid handler name.
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_AddsParameterDescriptors()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageWithHandlerParameters).GetTypeInfo();
|
||||
var expected = type.GetMethod(nameof(PageWithHandlerParameters.OnPost), BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
var handler = Assert.Single(results);
|
||||
|
||||
Assert.Collection(
|
||||
handler.Parameters,
|
||||
p =>
|
||||
{
|
||||
Assert.Equal(typeof(string), p.ParameterType);
|
||||
Assert.NotNull(p.ParameterInfo);
|
||||
Assert.Equal("name", p.Name);
|
||||
},
|
||||
p =>
|
||||
{
|
||||
Assert.Equal(typeof(int), p.ParameterType);
|
||||
Assert.NotNull(p.ParameterInfo);
|
||||
Assert.Equal("id", p.Name);
|
||||
Assert.Equal("personId", p.BindingInfo.BinderModelName);
|
||||
});
|
||||
}
|
||||
|
||||
private class PageWithHandlerParameters
|
||||
{
|
||||
public void OnPost(string name, [ModelBinder(Name = "personId")] int id) { }
|
||||
}
|
||||
|
||||
// We're using PropertyHelper from Common to find the properties here, which implements
|
||||
// out standard set of semantics for properties that the framework interacts with.
|
||||
//
|
||||
// One of the desirable consequences of that is we only find 'visible' properties. We're not
|
||||
// retesting all of the details of PropertyHelper here, just the visibility part as a quick check
|
||||
// that we're using PropertyHelper as expected.
|
||||
[Fact]
|
||||
public void CreateBoundProperties_UsesPropertyHelpers_ToFindProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(HidesAProperty).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateBoundProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(p => p.Property.Name),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal(typeof(HidesAProperty).GetTypeInfo(), p.Property.DeclaringType.GetTypeInfo());
|
||||
});
|
||||
}
|
||||
|
||||
private class HasAHiddenProperty
|
||||
{
|
||||
[BindProperty]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
private class HidesAProperty : HasAHiddenProperty
|
||||
{
|
||||
[BindProperty]
|
||||
public new int Property { get; set; }
|
||||
}
|
||||
|
||||
// We're using BindingInfo to make property binding opt-in here. We're not going to retest
|
||||
// all of the semantics of BindingInfo here, as that's covered elsewhere.
|
||||
[Fact]
|
||||
public void CreateBoundProperties_UsesBindingInfo_ToFindProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ModelWithBindingInfoProperty).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateBoundProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(p => p.Property.Name),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal("Property", p.Property.Name);
|
||||
});
|
||||
}
|
||||
|
||||
private class ModelWithBindingInfoProperty
|
||||
{
|
||||
[ModelBinder]
|
||||
public int Property { get; set; }
|
||||
|
||||
public int IgnoreMe { get; set; }
|
||||
}
|
||||
|
||||
// Additionally [BindProperty] on a property can opt-in a property
|
||||
[Fact]
|
||||
public void CreateBoundProperties_UsesBindPropertyAttribute_ToFindProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ModelWithBindProperty).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateBoundProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(p => p.Property.Name),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal("Property", p.Property.Name);
|
||||
});
|
||||
}
|
||||
|
||||
private class ModelWithBindProperty
|
||||
{
|
||||
[BindProperty]
|
||||
public int Property { get; set; }
|
||||
|
||||
public int IgnoreMe { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateBoundProperties_SupportsGet_OnProperty()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ModelSupportsGetOnProperty).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateBoundProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(p => p.Property.Name),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal("Property", p.Property.Name);
|
||||
Assert.NotNull(p.BindingInfo.RequestPredicate);
|
||||
Assert.True(p.BindingInfo.RequestPredicate(new ActionContext()
|
||||
{
|
||||
HttpContext = new DefaultHttpContext()
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method ="GET",
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private class ModelSupportsGetOnProperty
|
||||
{
|
||||
[BindProperty(SupportsGet = true)]
|
||||
public int Property { get; set; }
|
||||
|
||||
public int IgnoreMe { get; set; }
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Foo")]
|
||||
[InlineData("On")]
|
||||
[InlineData("OnAsync")]
|
||||
[InlineData("Async")]
|
||||
public void TryParseHandler_ParsesHandlerNames_InvalidData(string methodName)
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.TryParseHandlerMethod(methodName, out var httpMethod, out var handler);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(httpMethod);
|
||||
Assert.Null(handler);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("OnG", "G", null)]
|
||||
[InlineData("OnGAsync", "G", null)]
|
||||
[InlineData("OnPOST", "P", "OST")]
|
||||
[InlineData("OnPOSTAsync", "P", "OST")]
|
||||
[InlineData("OnDeleteFoo", "Delete", "Foo")]
|
||||
[InlineData("OnDeleteFooAsync", "Delete", "Foo")]
|
||||
[InlineData("OnMadeupLongHandlerName", "Madeup", "LongHandlerName")]
|
||||
[InlineData("OnMadeupLongHandlerNameAsync", "Madeup", "LongHandlerName")]
|
||||
public void TryParseHandler_ParsesHandlerNames_ValidData(string methodName, string expectedHttpMethod, string expectedHandler)
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.TryParseHandlerMethod(methodName, out var httpMethod, out var handler);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedHttpMethod, httpMethod);
|
||||
Assert.Equal(expectedHandler, handler);
|
||||
var compiler = new Mock<IViewCompiler>();
|
||||
compiler.Setup(c => c.CompileAsync(It.IsAny<string>()))
|
||||
.ReturnsAsync(descriptor);
|
||||
var compilerProvider = new Mock<IViewCompilerProvider>();
|
||||
compilerProvider.Setup(p => p.GetCompiler())
|
||||
.Returns(compiler.Object);
|
||||
return compilerProvider.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
|
@ -18,11 +17,9 @@ using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -235,7 +232,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
loader.Object,
|
||||
CreateActionDescriptorCollection(descriptor));
|
||||
|
||||
var context = new ActionInvokerProviderContext(new ActionContext()
|
||||
var context = new ActionInvokerProviderContext(new ActionContext
|
||||
{
|
||||
ActionDescriptor = descriptor,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
|
|
@ -251,6 +248,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var entry1 = actionInvoker.CacheEntry;
|
||||
|
||||
// Act - 2
|
||||
context = new ActionInvokerProviderContext(new ActionContext
|
||||
{
|
||||
ActionDescriptor = descriptor,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
RouteData = new RouteData(),
|
||||
});
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert - 2
|
||||
|
|
@ -445,6 +448,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
HandlerTypeInfo = modelTypeInfo ?? pageTypeInfo,
|
||||
ModelTypeInfo = modelTypeInfo ?? pageTypeInfo,
|
||||
PageTypeInfo = pageTypeInfo,
|
||||
FilterDescriptors = Array.Empty<FilterDescriptor>(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +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 Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class PageFilterApplicationModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_AddsFiltersToModels()
|
||||
{
|
||||
// Arrange
|
||||
var applicationModel1 = new PageApplicationModel("/Home.cshtml", "/Home.cshtml");
|
||||
var applicationModel2 = new PageApplicationModel("/About.cshtml", "/About.cshtml");
|
||||
var modelProvider = new PageFilterApplicationModelProvider();
|
||||
var context = new PageApplicationModelProviderContext
|
||||
{
|
||||
Results =
|
||||
{
|
||||
applicationModel1,
|
||||
applicationModel2,
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
modelProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(applicationModel1.Filters,
|
||||
filter => Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filter),
|
||||
filter => Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filter));
|
||||
|
||||
Assert.Collection(applicationModel2.Filters,
|
||||
filter => Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filter),
|
||||
filter => Assert.IsType<AutoValidateAntiforgeryTokenAttribute>(filter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// 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.
|
||||
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public partial class DefaultPageApplicationModelProviderTest
|
||||
{
|
||||
private class PocoModel
|
||||
{
|
||||
// Just a plain ol' model, nothing to see here.
|
||||
|
||||
[ModelBinder]
|
||||
public int IgnoreMe { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class RazorProjectPageApplicationModelProviderTest
|
||||
public class RazorProjectPageRouteModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_ReturnsPagesWithPageDirective()
|
||||
|
|
@ -27,14 +27,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var optionsManager = new TestOptionsManager<RazorPagesOptions>();
|
||||
optionsManager.Value.RootDirectory = "/";
|
||||
var provider = new RazorProjectPageApplicationModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new RazorProjectPageRouteModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.Results,
|
||||
Assert.Collection(context.RouteModels,
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Pages/Home.cshtml", model.RelativePath);
|
||||
|
|
@ -61,14 +61,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var optionsManager = new TestOptionsManager<RazorPagesOptions>();
|
||||
optionsManager.Value.RootDirectory = "/";
|
||||
var provider = new RazorProjectPageApplicationModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new RazorProjectPageRouteModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.Results,
|
||||
Assert.Collection(context.RouteModels,
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Pages/Admin/Index.cshtml", model.RelativePath);
|
||||
|
|
@ -99,12 +99,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var optionsManager = new TestOptionsManager<RazorPagesOptions>();
|
||||
optionsManager.Value.RootDirectory = "/";
|
||||
var provider = new RazorProjectPageApplicationModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new RazorProjectPageRouteModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
|
||||
Assert.Equal("The route for the page at '/Index.cshtml' cannot start with / or ~/. Pages do not support overriding the file path of the page.",
|
||||
Assert.Equal("The route for the page at '/Index.cshtml' cannot start with / or ~/. Pages do not support overriding the file path of the page.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
|
|
@ -125,14 +125,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var optionsManager = new TestOptionsManager<RazorPagesOptions>();
|
||||
optionsManager.Value.RootDirectory = "/";
|
||||
var provider = new RazorProjectPageApplicationModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new RazorProjectPageRouteModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.Results,
|
||||
Assert.Collection(context.RouteModels,
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Pages/Home.cshtml", model.RelativePath);
|
||||
|
|
@ -163,14 +163,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var optionsManager = new TestOptionsManager<RazorPagesOptions>();
|
||||
optionsManager.Value.RootDirectory = "/Pages";
|
||||
var provider = new RazorProjectPageApplicationModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageApplicationModelProviderContext();
|
||||
var provider = new RazorProjectPageRouteModelProvider(project, optionsManager, NullLoggerFactory.Instance);
|
||||
var context = new PageRouteModelProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(context.Results,
|
||||
Assert.Collection(context.RouteModels,
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Pages/Index.cshtml", model.RelativePath);
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// 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.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class TempDataFilterPageApplicationModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_AddsFiltersToModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new PageActionDescriptor();
|
||||
var applicationModel = new PageApplicationModel(
|
||||
actionDescriptor,
|
||||
typeof(object).GetTypeInfo(),
|
||||
new object[0]);
|
||||
var applicationModelProvider = new TempDataFilterPageApplicationModelProvider();
|
||||
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeof(object).GetTypeInfo())
|
||||
{
|
||||
PageApplicationModel = applicationModel,
|
||||
};
|
||||
|
||||
// Act
|
||||
applicationModelProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
applicationModel.Filters,
|
||||
filter => Assert.IsType<PageSaveTempDataPropertyFilterFactory>(filter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -422,13 +422,22 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
typeof(JsonPatchOperationsArrayProvider),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(IPageRouteModelProvider),
|
||||
new[]
|
||||
{
|
||||
typeof(CompiledPageRouteModelProvider),
|
||||
typeof(RazorProjectPageRouteModelProvider),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(IPageApplicationModelProvider),
|
||||
new[]
|
||||
{
|
||||
typeof(CompiledPageApplicationModelProvider),
|
||||
typeof(RazorProjectPageApplicationModelProvider),
|
||||
typeof(PageFilterApplicationModelProvider),
|
||||
typeof(AuthorizationPageApplicationModelProvider),
|
||||
typeof(AuthorizationPageApplicationModelProvider),
|
||||
typeof(DefaultPageApplicationModelProvider),
|
||||
typeof(TempDataFilterPageApplicationModelProvider),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
// 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.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
[Authorize]
|
||||
public class ModelWithAuthFilter : PageModel
|
||||
{
|
||||
public IActionResult OnGet() => Page();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@page
|
||||
@model RazorPagesWebSite.ModelWithAuthFilter
|
||||
|
||||
Can't see me
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
[HandlerChangingPageFilter]
|
||||
public class ModelWithPageFilter
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Message = $"Hello from {nameof(OnGet)}";
|
||||
}
|
||||
|
||||
public void OnGetEdit()
|
||||
{
|
||||
Message = $"Hello from {nameof(OnGetEdit)}";
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class HandlerChangingPageFilterAttribute : Attribute, IPageFilter
|
||||
{
|
||||
public void OnPageHandlerSelected(PageHandlerSelectedContext context)
|
||||
{
|
||||
context.HandlerMethod = context.ActionDescriptor.HandlerMethods.First(m => m.Name == "Edit");
|
||||
}
|
||||
|
||||
public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@page
|
||||
@model RazorPagesWebSite.ModelWithPageFilter
|
||||
|
||||
@Model.Message
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class AnonymousModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@page
|
||||
@model AnonymousModel
|
||||
Hello from Anonymous
|
||||
Loading…
Reference in New Issue