Design updates to handler methods and binding
Implements #6091, #6072, #6090, #5927
This commit is contained in:
parent
908b4c8d62
commit
297196baa0
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public class BindPropertyAttribute : Attribute, IModelNameProvider, IBinderTypeProviderMetadata
|
||||
{
|
||||
private BindingSource _bindingSource;
|
||||
|
||||
public bool SupportsGet { get; set; }
|
||||
|
||||
public Type BinderType { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual BindingSource BindingSource
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bindingSource == null && BinderType != null)
|
||||
{
|
||||
return BindingSource.Custom;
|
||||
}
|
||||
|
||||
return _bindingSource;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
_bindingSource = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -30,15 +30,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TypeInfo"/> of the page.
|
||||
/// Gets the list of handler methods for the page.
|
||||
/// </summary>
|
||||
public TypeInfo PageTypeInfo { get; set; }
|
||||
public IList<HandlerMethodDescriptor> HandlerMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TypeInfo"/> of the type that defines handler methods for the page. This can be
|
||||
/// the same as <see cref="PageTypeInfo"/> and <see cref="ModelTypeInfo"/> if the page does not have an
|
||||
/// explicit model type defined.
|
||||
/// </summary>
|
||||
public TypeInfo HandlerTypeInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TypeInfo"/> of the model.
|
||||
/// </summary>
|
||||
public TypeInfo ModelTypeInfo { get; set; }
|
||||
|
||||
public IList<HandlerMethodDescriptor> HandlerMethods { get; } = new List<HandlerMethodDescriptor>();
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TypeInfo"/> of the page.
|
||||
/// </summary>
|
||||
public TypeInfo PageTypeInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,19 @@
|
|||
// 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 System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
public class HandlerMethodDescriptor
|
||||
{
|
||||
public MethodInfo Method { get; set; }
|
||||
|
||||
public Func<object, object[], Task<IActionResult>> Executor { get; set; }
|
||||
public MethodInfo MethodInfo { get; set; }
|
||||
|
||||
public string HttpMethod { get; set; }
|
||||
|
||||
public StringSegment FormAction { get; set; }
|
||||
public string FormAction { get; set; }
|
||||
|
||||
public HandlerParameterDescriptor[] Parameters { get; set; }
|
||||
|
||||
public bool OnPage { get; set; }
|
||||
public IList<HandlerParameterDescriptor> Parameters { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
|
||||
|
|
@ -9,8 +8,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
public class HandlerParameterDescriptor : ParameterDescriptor
|
||||
{
|
||||
public object DefaultValue { get; set; }
|
||||
|
||||
public ParameterInfo Parameter { get; set; }
|
||||
public ParameterInfo ParameterInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
public class PageBoundPropertyDescriptor : ParameterDescriptor
|
||||
{
|
||||
public PropertyInfo Property { get; set; }
|
||||
|
||||
public bool SupportsGet { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute for base classes for Pages and PageModels. Applying this attribute to a type
|
||||
/// suppresses discovery of handler methods and bound properties for that type.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public class PagesBaseClassAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
|
||||
if (ambiguousMatches != null)
|
||||
{
|
||||
var ambiguousMethods = string.Join(", ", ambiguousMatches.Select(m => m.Method));
|
||||
var ambiguousMethods = string.Join(", ", ambiguousMatches.Select(m => m.MethodInfo));
|
||||
throw new InvalidOperationException(Resources.FormatAmbiguousHandler(Environment.NewLine, ambiguousMethods));
|
||||
}
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
continue;
|
||||
}
|
||||
else if (handler.FormAction.HasValue &&
|
||||
else if (handler.FormAction != null &&
|
||||
!handler.FormAction.Equals(formAction, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
// 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.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
|
|
@ -20,21 +24,249 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
public CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor)
|
||||
{
|
||||
var compilationResult = _compiler.Compile(actionDescriptor.RelativePath);
|
||||
var compiledTypeInfo = compilationResult.CompiledType.GetTypeInfo();
|
||||
// If a model type wasn't set in code then the model property's type will be the same
|
||||
// as the compiled type.
|
||||
var modelTypeInfo = compiledTypeInfo.GetProperty(ModelPropertyName)?.PropertyType.GetTypeInfo();
|
||||
if (modelTypeInfo == compiledTypeInfo)
|
||||
var result = _compiler.Compile(actionDescriptor.RelativePath);
|
||||
return CreateDescriptor(actionDescriptor, result.CompiledType.GetTypeInfo());
|
||||
}
|
||||
|
||||
// Internal for unit testing
|
||||
internal static CompiledPageActionDescriptor CreateDescriptor(PageActionDescriptor actionDescriptor, TypeInfo pageType)
|
||||
{
|
||||
// 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.
|
||||
//
|
||||
// However, we allow it to be null here for ease of testing.
|
||||
var modelType = pageType.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)
|
||||
{
|
||||
modelTypeInfo = null;
|
||||
handlerType = modelType;
|
||||
}
|
||||
else
|
||||
{
|
||||
handlerType = pageType;
|
||||
handlerMethods = CreateHandlerMethods(pageType);
|
||||
}
|
||||
|
||||
var boundProperties = CreateBoundProperties(handlerType);
|
||||
|
||||
return new CompiledPageActionDescriptor(actionDescriptor)
|
||||
{
|
||||
PageTypeInfo = compiledTypeInfo,
|
||||
ModelTypeInfo = modelTypeInfo,
|
||||
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 formAction))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var parameters = CreateHandlerParameters(method);
|
||||
|
||||
var handlerMethodDescriptor = new HandlerMethodDescriptor()
|
||||
{
|
||||
MethodInfo = method,
|
||||
FormAction = formAction,
|
||||
HttpMethod = httpMethod,
|
||||
Parameters = parameters,
|
||||
};
|
||||
|
||||
results.Add(handlerMethodDescriptor);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
// If the type has a [BindPropertyAttribute] then we'll consider any and all public properties bindable.
|
||||
var bindPropertyOnType = type.GetCustomAttribute<BindPropertyAttribute>();
|
||||
|
||||
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 (bindingInfo == null && bindPropertyOnType == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property.Property.DeclaringType.GetTypeInfo().IsDefined(typeof(PagesBaseClassAttribute)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var bindPropertyOnProperty = property.Property.GetCustomAttribute<BindPropertyAttribute>();
|
||||
var supportsGet = bindPropertyOnProperty?.SupportsGet ?? bindPropertyOnType?.SupportsGet ?? false;
|
||||
|
||||
var descriptor = new PageBoundPropertyDescriptor()
|
||||
{
|
||||
BindingInfo = bindingInfo ?? new BindingInfo(),
|
||||
Name = property.Name,
|
||||
Property = property.Property,
|
||||
ParameterType = property.Property.PropertyType,
|
||||
SupportsGet = supportsGet,
|
||||
};
|
||||
|
||||
results.Add(descriptor);
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -12,38 +13,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
public static class ExecutorFactory
|
||||
{
|
||||
public static Func<object, object[], Task<IActionResult>> CreateExecutor(
|
||||
CompiledPageActionDescriptor actionDescriptor,
|
||||
MethodInfo method,
|
||||
HandlerParameterDescriptor[] parameters)
|
||||
public static Func<object, object[], Task<IActionResult>> CreateExecutor(HandlerMethodDescriptor handlerDescriptor)
|
||||
{
|
||||
if (actionDescriptor == null)
|
||||
if (handlerDescriptor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actionDescriptor));
|
||||
throw new ArgumentNullException(nameof(handlerDescriptor));
|
||||
}
|
||||
|
||||
var handler = CreateHandlerMethod(handlerDescriptor);
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(method));
|
||||
}
|
||||
|
||||
if (parameters == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameters));
|
||||
}
|
||||
|
||||
var methodIsDeclaredOnPage = method.DeclaringType.GetTypeInfo().IsAssignableFrom(actionDescriptor.PageTypeInfo);
|
||||
var handler = CreateHandlerMethod(method, parameters);
|
||||
|
||||
return async (receiver, arguments) =>
|
||||
{
|
||||
var result = await handler.Execute(receiver, arguments);
|
||||
return result;
|
||||
};
|
||||
return handler.Execute;
|
||||
}
|
||||
|
||||
private static HandlerMethod CreateHandlerMethod(MethodInfo method, HandlerParameterDescriptor[] parameters)
|
||||
private static HandlerMethod CreateHandlerMethod(HandlerMethodDescriptor handlerDescriptor)
|
||||
{
|
||||
var method = handlerDescriptor.MethodInfo;
|
||||
var parameters = handlerDescriptor.Parameters.ToArray();
|
||||
|
||||
var methodParameters = method.GetParameters();
|
||||
|
||||
var returnType = method.ReturnType;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
|
|
@ -327,7 +328,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
_pageContext.ViewStarts = viewStarts;
|
||||
|
||||
if (actionDescriptor.ModelTypeInfo == null)
|
||||
if (actionDescriptor.ModelTypeInfo == actionDescriptor.PageTypeInfo)
|
||||
{
|
||||
_model = _page;
|
||||
}
|
||||
|
|
@ -341,10 +342,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
_pageContext.ViewData.Model = _model;
|
||||
}
|
||||
|
||||
if (CacheEntry.PropertyBinder != null &&
|
||||
!string.Equals(_pageContext.HttpContext.Request.Method, "GET", StringComparison.OrdinalIgnoreCase))
|
||||
if (CacheEntry.PropertyBinder != null)
|
||||
{
|
||||
// Don't bind properties on GET requests
|
||||
await CacheEntry.PropertyBinder(_page, _model);
|
||||
}
|
||||
|
||||
|
|
@ -379,8 +378,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
var arguments = await GetArguments(handler);
|
||||
|
||||
var executor = handler.Executor;
|
||||
result = await executor(handler.OnPage ? _page : _model, arguments);
|
||||
Func<object, object[], Task<IActionResult>> executor = null;
|
||||
for (var i = 0; i < actionDescriptor.HandlerMethods.Count; i++)
|
||||
{
|
||||
if (object.ReferenceEquals(handler, actionDescriptor.HandlerMethods[i]))
|
||||
{
|
||||
executor = CacheEntry.Executors[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var instance = actionDescriptor.ModelTypeInfo == actionDescriptor.HandlerTypeInfo ? _model : _page;
|
||||
result = await executor(instance, arguments);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
|
|
@ -393,10 +402,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
private async Task<object[]> GetArguments(HandlerMethodDescriptor handler)
|
||||
{
|
||||
var arguments = new object[handler.Parameters.Length];
|
||||
var arguments = new object[handler.Parameters.Count];
|
||||
var valueProvider = await CompositeValueProvider.CreateAsync(_pageContext, _pageContext.ValueProviderFactories);
|
||||
|
||||
for (var i = 0; i < handler.Parameters.Length; i++)
|
||||
for (var i = 0; i < handler.Parameters.Count; i++)
|
||||
{
|
||||
var parameter = handler.Parameters[i];
|
||||
|
||||
|
|
@ -406,7 +415,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
parameter,
|
||||
value: null);
|
||||
|
||||
arguments[i] = result.IsModelSet ? result.Model : parameter.DefaultValue;
|
||||
if (result.IsModelSet)
|
||||
{
|
||||
arguments[i] = result.Model;
|
||||
}
|
||||
else if (parameter.ParameterInfo.HasDefaultValue)
|
||||
{
|
||||
arguments[i] = parameter.ParameterInfo.DefaultValue;
|
||||
}
|
||||
else if (parameter.ParameterType.GetTypeInfo().IsValueType)
|
||||
{
|
||||
arguments[i] = Activator.CreateInstance(parameter.ParameterType);
|
||||
}
|
||||
}
|
||||
|
||||
return arguments;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
|
|
@ -19,6 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Func<PageContext, object> modelFactory,
|
||||
Action<PageContext, object> releaseModel,
|
||||
Func<Page, object, Task> propertyBinder,
|
||||
Func<object, object[], Task<IActionResult>>[] executors,
|
||||
IReadOnlyList<Func<IRazorPage>> viewStartFactories,
|
||||
FilterItem[] cacheableFilters)
|
||||
{
|
||||
|
|
@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
ModelFactory = modelFactory;
|
||||
ReleaseModel = releaseModel;
|
||||
PropertyBinder = propertyBinder;
|
||||
Executors = executors;
|
||||
ViewStartFactories = viewStartFactories;
|
||||
CacheableFilters = cacheableFilters;
|
||||
}
|
||||
|
|
@ -54,6 +55,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
/// </summary>
|
||||
public Func<Page, object, Task> PropertyBinder { get; }
|
||||
|
||||
public Func<object, object[], Task<IActionResult>>[] Executors { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the applicable ViewStart pages.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
|
@ -181,20 +182,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
Func<PageContext, object> modelFactory = null;
|
||||
Action<PageContext, object> modelReleaser = null;
|
||||
if (compiledActionDescriptor.ModelTypeInfo == null)
|
||||
if (compiledActionDescriptor.ModelTypeInfo != compiledActionDescriptor.PageTypeInfo)
|
||||
{
|
||||
PopulateHandlerMethodDescriptors(compiledActionDescriptor.PageTypeInfo, compiledActionDescriptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
PopulateHandlerMethodDescriptors(compiledActionDescriptor.ModelTypeInfo, compiledActionDescriptor);
|
||||
|
||||
modelFactory = _modelFactoryProvider.CreateModelFactory(compiledActionDescriptor);
|
||||
modelReleaser = _modelFactoryProvider.CreateModelDisposer(compiledActionDescriptor);
|
||||
}
|
||||
|
||||
var viewStartFactories = GetViewStartFactories(compiledActionDescriptor);
|
||||
|
||||
var executors = GetExecutors(compiledActionDescriptor);
|
||||
|
||||
return new PageActionInvokerCacheEntry(
|
||||
compiledActionDescriptor,
|
||||
pageFactory,
|
||||
|
|
@ -202,6 +199,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
modelFactory,
|
||||
modelReleaser,
|
||||
propertyBinder,
|
||||
executors,
|
||||
viewStartFactories,
|
||||
cachedFilters);
|
||||
}
|
||||
|
|
@ -226,82 +224,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
return viewStartFactories;
|
||||
}
|
||||
|
||||
// Internal for testing.
|
||||
internal static void PopulateHandlerMethodDescriptors(TypeInfo type, CompiledPageActionDescriptor actionDescriptor)
|
||||
{
|
||||
var methods = type.GetMethods();
|
||||
for (var i = 0; i < methods.Length; i++)
|
||||
{
|
||||
var method = methods[i];
|
||||
if (!IsValidHandler(method))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string httpMethod;
|
||||
int formActionStart;
|
||||
|
||||
if (method.Name.StartsWith("OnGet", StringComparison.Ordinal))
|
||||
{
|
||||
httpMethod = "GET";
|
||||
formActionStart = "OnGet".Length;
|
||||
}
|
||||
else if (method.Name.StartsWith("OnPost", StringComparison.Ordinal))
|
||||
{
|
||||
httpMethod = "POST";
|
||||
formActionStart = "OnPost".Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var formActionLength = method.Name.Length - formActionStart;
|
||||
if (method.Name.EndsWith("Async", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
formActionLength -= "Async".Length;
|
||||
}
|
||||
|
||||
var formAction = new StringSegment(method.Name, formActionStart, formActionLength);
|
||||
|
||||
var parameters = GetHandlerParameters(method);
|
||||
|
||||
var handlerMethodDescriptor = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = method,
|
||||
Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method, parameters),
|
||||
FormAction = formAction,
|
||||
HttpMethod = httpMethod,
|
||||
Parameters = parameters,
|
||||
OnPage = actionDescriptor.PageTypeInfo == type,
|
||||
};
|
||||
|
||||
actionDescriptor.HandlerMethods.Add(handlerMethodDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private static HandlerParameterDescriptor[] GetHandlerParameters(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()),
|
||||
DefaultValue = GetDefaultValue(parameter),
|
||||
Name = parameter.Name,
|
||||
Parameter = parameter,
|
||||
ParameterType = parameter.ParameterType,
|
||||
};
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static object GetDefaultValue(ParameterInfo methodParameter)
|
||||
{
|
||||
object defaultValue = null;
|
||||
|
|
@ -317,42 +239,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
return defaultValue;
|
||||
}
|
||||
|
||||
private static bool IsValidHandler(MethodInfo methodInfo)
|
||||
private static Func<object, object[], Task<IActionResult>>[] GetExecutors(CompiledPageActionDescriptor actionDescriptor)
|
||||
{
|
||||
// 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)
|
||||
if (actionDescriptor.HandlerMethods == null || actionDescriptor.HandlerMethods.Count == 0)
|
||||
{
|
||||
return false;
|
||||
return Array.Empty<Func<object, object[], Task<IActionResult>>>();
|
||||
}
|
||||
|
||||
// Overriden methods from Object class, e.g. Equals(Object), GetHashCode(), etc., are not valid.
|
||||
if (methodInfo.GetBaseDefinition().DeclaringType == typeof(object))
|
||||
var results = new Func<object, object[], Task<IActionResult>>[actionDescriptor.HandlerMethods.Count];
|
||||
|
||||
for (var i = 0; i < actionDescriptor.HandlerMethods.Count; i++)
|
||||
{
|
||||
return false;
|
||||
results[i] = ExecutorFactory.CreateExecutor(actionDescriptor.HandlerMethods[i]);
|
||||
}
|
||||
|
||||
if (methodInfo.IsStatic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsConstructor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsGenericMethod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return methodInfo.IsPublic;
|
||||
return results;
|
||||
}
|
||||
|
||||
internal class InnerCache
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
|
|
@ -29,15 +28,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
throw new ArgumentNullException(nameof(actionDescriptor));
|
||||
}
|
||||
|
||||
var bindPropertiesOnPage = actionDescriptor.ModelTypeInfo == null;
|
||||
var target = bindPropertiesOnPage ? actionDescriptor.PageTypeInfo : actionDescriptor.ModelTypeInfo;
|
||||
var propertiesToBind = GetPropertiesToBind(modelMetadataProvider, target);
|
||||
|
||||
if (propertiesToBind.Count == 0)
|
||||
var properties = actionDescriptor.BoundProperties;
|
||||
if (properties == null || properties.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var isHandlerThePage = actionDescriptor.HandlerTypeInfo == actionDescriptor.PageTypeInfo;
|
||||
|
||||
var type = actionDescriptor.HandlerTypeInfo.AsType();
|
||||
var metadata = new ModelMetadata[properties.Count];
|
||||
for (var i = 0; i < properties.Count; i++)
|
||||
{
|
||||
metadata[i] = modelMetadataProvider.GetMetadataForProperty(type, properties[i].Name);
|
||||
}
|
||||
|
||||
return Bind;
|
||||
|
||||
Task Bind(Page page, object model)
|
||||
|
|
@ -47,14 +52,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
throw new ArgumentNullException(nameof(page));
|
||||
}
|
||||
|
||||
if (!bindPropertiesOnPage && model == null)
|
||||
if (!isHandlerThePage && model == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
}
|
||||
|
||||
var pageContext = page.PageContext;
|
||||
var instance = bindPropertiesOnPage ? page : model;
|
||||
return BindPropertiesAsync(parameterBinder, pageContext, instance, propertiesToBind);
|
||||
var instance = isHandlerThePage ? page : model;
|
||||
return BindPropertiesAsync(parameterBinder, pageContext, instance, properties, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,92 +67,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
ParameterBinder parameterBinder,
|
||||
PageContext pageContext,
|
||||
object instance,
|
||||
IList<PropertyBindingInfo> propertiesToBind)
|
||||
IList<ParameterDescriptor> properties,
|
||||
IList<ModelMetadata> metadata)
|
||||
{
|
||||
var valueProvider = await GetCompositeValueProvider(pageContext);
|
||||
for (var i = 0; i < propertiesToBind.Count; i++)
|
||||
var isGet = string.Equals("GET", pageContext.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var valueProvider = await CompositeValueProvider.CreateAsync(pageContext, pageContext.ValueProviderFactories);
|
||||
for (var i = 0; i < properties.Count; i++)
|
||||
{
|
||||
var propertyBindingInfo = propertiesToBind[i];
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(
|
||||
pageContext,
|
||||
valueProvider,
|
||||
propertyBindingInfo.ParameterDescriptor);
|
||||
if (modelBindingResult.IsModelSet)
|
||||
{
|
||||
var modelMetadata = propertyBindingInfo.ModelMetadata;
|
||||
PropertyValueSetter.SetValue(
|
||||
modelMetadata,
|
||||
instance,
|
||||
modelBindingResult.Model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IList<PropertyBindingInfo> GetPropertiesToBind(
|
||||
IModelMetadataProvider modelMetadataProvider,
|
||||
TypeInfo handlerSourceTypeInfo)
|
||||
{
|
||||
var handlerType = handlerSourceTypeInfo.AsType();
|
||||
var properties = PropertyHelper.GetVisibleProperties(type: handlerType);
|
||||
var typeMetadata = modelMetadataProvider.GetMetadataForType(handlerType);
|
||||
|
||||
var propertyBindingInfo = new List<PropertyBindingInfo>();
|
||||
for (var i = 0; i < properties.Length; i++)
|
||||
{
|
||||
var property = properties[i];
|
||||
var bindingInfo = BindingInfo.GetBindingInfo(property.Property.GetCustomAttributes());
|
||||
|
||||
if (bindingInfo == null)
|
||||
if (isGet && !((PageBoundPropertyDescriptor)properties[i]).SupportsGet)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var propertyMetadata = typeMetadata.Properties[property.Name] ??
|
||||
modelMetadataProvider.GetMetadataForProperty(handlerType, property.Name);
|
||||
if (propertyMetadata == null)
|
||||
var result = await parameterBinder.BindModelAsync(pageContext, valueProvider, properties[i]);
|
||||
if (result.IsModelSet)
|
||||
{
|
||||
continue;
|
||||
PropertyValueSetter.SetValue(metadata[i], instance, result.Model);
|
||||
}
|
||||
|
||||
var parameterDescriptor = new ParameterDescriptor
|
||||
{
|
||||
BindingInfo = bindingInfo,
|
||||
Name = property.Name,
|
||||
ParameterType = property.Property.PropertyType,
|
||||
};
|
||||
|
||||
propertyBindingInfo.Add(new PropertyBindingInfo(parameterDescriptor, propertyMetadata));
|
||||
}
|
||||
|
||||
return propertyBindingInfo;
|
||||
}
|
||||
|
||||
private static async Task<CompositeValueProvider> GetCompositeValueProvider(PageContext pageContext)
|
||||
{
|
||||
var factories = pageContext.ValueProviderFactories;
|
||||
var valueProviderFactoryContext = new ValueProviderFactoryContext(pageContext);
|
||||
for (var i = 0; i < factories.Count; i++)
|
||||
{
|
||||
var factory = factories[i];
|
||||
await factory.CreateValueProviderAsync(valueProviderFactoryContext);
|
||||
}
|
||||
|
||||
return new CompositeValueProvider(valueProviderFactoryContext.ValueProviders);
|
||||
}
|
||||
|
||||
private struct PropertyBindingInfo
|
||||
{
|
||||
public PropertyBindingInfo(
|
||||
ParameterDescriptor parameterDescriptor,
|
||||
ModelMetadata modelMetadata)
|
||||
{
|
||||
ParameterDescriptor = parameterDescriptor;
|
||||
ModelMetadata = modelMetadata;
|
||||
}
|
||||
|
||||
public ParameterDescriptor ParameterDescriptor { get; }
|
||||
|
||||
public ModelMetadata ModelMetadata { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that the targeted method is not a page handler method.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public class NonHandlerAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// <summary>
|
||||
/// A base class for a Razor page.
|
||||
/// </summary>
|
||||
[PagesBaseClass]
|
||||
public abstract class Page : RazorPageBase, IRazorPage
|
||||
{
|
||||
private IObjectModelValidator _objectValidator;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ using Microsoft.Net.Http.Headers;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
[PagesBaseClass]
|
||||
public abstract class PageModel
|
||||
{
|
||||
private IObjectModelValidator _objectValidator;
|
||||
|
|
|
|||
|
|
@ -716,6 +716,22 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.DoesNotContain(validationError, content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PageProperty_WithSupportsGet_BoundInGet()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "<p>11</p>";
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "Pages/PropertyBinding/BindPropertyWithGet?value=11");
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
Assert.StartsWith(expected, content.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PagePropertiesAreInjected()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
|
@ -30,7 +32,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -67,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor,
|
||||
},
|
||||
|
|
@ -109,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -140,20 +142,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
FormAction = "Add",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Delete"),
|
||||
FormAction = "Delete",
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -190,20 +192,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
FormAction = "Add",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Delete"),
|
||||
FormAction = "Delete",
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -240,20 +242,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
FormAction = "Add",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Delete"),
|
||||
FormAction = "Delete",
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -285,20 +287,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
FormAction = "Add",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Delete"),
|
||||
FormAction = "Delete",
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -336,20 +338,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
FormAction = "Add",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Delete"),
|
||||
FormAction = "Delete",
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -381,7 +383,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Subscribe"),
|
||||
FormAction = "Subscribe",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
|
|
@ -393,7 +395,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -429,13 +431,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = GetType().GetMethod(nameof(Post)),
|
||||
MethodInfo = GetType().GetMethod(nameof(Post)),
|
||||
HttpMethod = "POST",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = GetType().GetMethod(nameof(PostAsync)),
|
||||
MethodInfo = GetType().GetMethod(nameof(PostAsync)),
|
||||
HttpMethod = "POST",
|
||||
};
|
||||
|
||||
|
|
@ -448,7 +450,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -468,7 +470,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => selector.Select(pageContext));
|
||||
var methods = descriptor1.Method + ", " + descriptor2.Method;
|
||||
var methods = descriptor1.MethodInfo + ", " + descriptor2.MethodInfo;
|
||||
var message = "Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:" +
|
||||
Environment.NewLine + Environment.NewLine + methods;
|
||||
|
||||
|
|
@ -481,16 +483,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = GetType().GetMethod(nameof(Post)),
|
||||
MethodInfo = GetType().GetMethod(nameof(Post)),
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
FormAction = "Add",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = GetType().GetMethod(nameof(PostAsync)),
|
||||
MethodInfo = GetType().GetMethod(nameof(PostAsync)),
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
FormAction = "Add",
|
||||
};
|
||||
|
||||
var descriptor3 = new HandlerMethodDescriptor
|
||||
|
|
@ -502,7 +504,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
|
|
@ -528,7 +530,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => selector.Select(pageContext));
|
||||
var methods = descriptor1.Method + ", " + descriptor2.Method;
|
||||
var methods = descriptor1.MethodInfo + ", " + descriptor2.MethodInfo;
|
||||
var message = "Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:" +
|
||||
Environment.NewLine + Environment.NewLine + methods;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,676 @@
|
|||
// 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.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class DefaultPageLoaderTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateDescriptor_CopiesPropertiesFromBaseClass()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new PageActionDescriptor() // We only copy the properties that are meaningful for pages.
|
||||
{
|
||||
ActionConstraints = new List<IActionConstraintMetadata>(),
|
||||
AttributeRouteInfo = new AttributeRouteInfo(),
|
||||
FilterDescriptors = new List<FilterDescriptor>(),
|
||||
RelativePath = "/Foo",
|
||||
RouteValues = new Dictionary<string, string>(),
|
||||
ViewEnginePath = "/Pages/Foo",
|
||||
};
|
||||
|
||||
// Act
|
||||
var actual = DefaultPageLoader.CreateDescriptor(expected, typeof(EmptyPage).GetTypeInfo());
|
||||
|
||||
// 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).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.CreateDescriptor(new PageActionDescriptor(), type);
|
||||
|
||||
// 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).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.CreateDescriptor(new PageActionDescriptor(), 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).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.CreateDescriptor(new PageActionDescriptor(), 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).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var result = DefaultPageLoader.CreateDescriptor(new PageActionDescriptor(), 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() { }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateHandlerMethods_DiscoversHandlersFromBaseType()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(InheritsMethods).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(h => h.MethodInfo.Name).ToArray(),
|
||||
(handler) =>
|
||||
{
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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(PageModelWithFormActions).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateHandlerMethods(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(h => h.MethodInfo.Name),
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(type.GetMethod(nameof(PageModelWithFormActions.OnPutDeleteAsync)), handler.MethodInfo);
|
||||
Assert.Equal("Put", handler.HttpMethod);
|
||||
Assert.Equal("Delete", handler.FormAction.ToString());
|
||||
});
|
||||
}
|
||||
|
||||
private class PageModelWithFormActions
|
||||
{
|
||||
public void OnPutDeleteAsync()
|
||||
{
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
// Additionally [BindProperty] on a property can opt-in a property
|
||||
[Fact]
|
||||
public void CreateBoundProperties_BindPropertyAttributeOnModel_OptsInAllProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ModelWithBindPropertyOnClass).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateBoundProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(p => p.Property.Name),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal("Property", p.Property.Name);
|
||||
});
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
private class ModelWithBindPropertyOnClass : EmptyPageModel
|
||||
{
|
||||
public int Property { 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.True(p.SupportsGet);
|
||||
});
|
||||
}
|
||||
|
||||
private class ModelSupportsGetOnProperty
|
||||
{
|
||||
[BindProperty(SupportsGet = true)]
|
||||
public int Property { get; set; }
|
||||
|
||||
public int IgnoreMe { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateBoundProperties_SupportsGet_OnClass()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ModelSupportsGetOnClass).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateBoundProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(p => p.Property.Name),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal("Property", p.Property.Name);
|
||||
Assert.True(p.SupportsGet);
|
||||
});
|
||||
}
|
||||
|
||||
[BindProperty(SupportsGet = true)]
|
||||
private class ModelSupportsGetOnClass : EmptyPageModel
|
||||
{
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateBoundProperties_SupportsGet_Override()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ModelSupportsGetOverride).GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var results = DefaultPageLoader.CreateBoundProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results.OrderBy(p => p.Property.Name),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal("Property", p.Property.Name);
|
||||
Assert.False(p.SupportsGet);
|
||||
});
|
||||
}
|
||||
|
||||
[BindProperty(SupportsGet = true)]
|
||||
private class ModelSupportsGetOverride : EmptyPageModel
|
||||
{
|
||||
[BindProperty(SupportsGet = false)]
|
||||
public int Property { 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,17 +16,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
public class ExecutorFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForActionResultMethod_OnPage()
|
||||
public async Task CreateExecutor_ForActionResultMethod()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
var handler = new HandlerMethodDescriptor()
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
MethodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturningHandler)),
|
||||
Parameters = new HandlerParameterDescriptor[0],
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
||||
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
|
|
@ -36,17 +36,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForMethodReturningConcreteSubtypeOfIActionResult_OnPage()
|
||||
public async Task CreateExecutor_ForMethodReturningConcreteSubtypeOfIActionResult()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
var handler = new HandlerMethodDescriptor()
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
MethodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ConcreteActionResult)),
|
||||
Parameters = new HandlerParameterDescriptor[0],
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ConcreteActionResult));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
||||
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
|
|
@ -56,18 +56,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForActionResultReturningMethod_WithParameters_OnPage()
|
||||
public async Task CreateExecutor_ForActionResultReturningMethod_WithParameters()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturnHandlerWithParameters));
|
||||
var parameters = CreateParameters(methodInfo);
|
||||
var handler = new HandlerMethodDescriptor()
|
||||
{
|
||||
MethodInfo = methodInfo,
|
||||
Parameters = CreateParameters(methodInfo),
|
||||
};
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, parameters);
|
||||
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
|
|
@ -78,18 +78,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForVoidReturningMethod_OnPage()
|
||||
public async Task CreateExecutor_ForVoidReturningMethod()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
var handler = new HandlerMethodDescriptor()
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
MethodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidReturningHandler)),
|
||||
Parameters = new HandlerParameterDescriptor[0],
|
||||
};
|
||||
|
||||
var page = new TestPage();
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
||||
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
|
|
@ -100,18 +101,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForVoidTaskReturningMethod_OnPage()
|
||||
public async Task CreateExecutor_ForVoidTaskReturningMethod()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
var handler = new HandlerMethodDescriptor()
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
MethodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidTaskReturningHandler)),
|
||||
Parameters = new HandlerParameterDescriptor[0],
|
||||
};
|
||||
|
||||
var page = new TestPage();
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidTaskReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
||||
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
|
|
@ -122,17 +124,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForTaskOfIActionResultReturningMethod_OnPage()
|
||||
public async Task CreateExecutor_ForTaskOfIActionResultReturningMethod()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.GenericTaskHandler));
|
||||
var handler = new HandlerMethodDescriptor()
|
||||
{
|
||||
MethodInfo = methodInfo,
|
||||
Parameters = CreateParameters(methodInfo),
|
||||
};
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
|
||||
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
|
|
@ -142,17 +145,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForTaskOfConcreteActionResultReturningMethod_OnPage()
|
||||
public async Task CreateExecutor_ForTaskOfConcreteActionResultReturningMethod()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.TaskReturningConcreteSubtype));
|
||||
var handler = new HandlerMethodDescriptor()
|
||||
{
|
||||
MethodInfo = methodInfo,
|
||||
Parameters = CreateParameters(methodInfo),
|
||||
};
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
|
||||
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
|
|
@ -162,159 +166,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
Assert.Equal("value", contentResult.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForActionResultMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPageModel(), null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<EmptyResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForMethodReturningConcreteSubtypeOfIActionResult_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ConcreteActionResult));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPageModel(), null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<ViewResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForActionResultReturningMethod_WithParameters_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturnHandlerWithParameters));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPageModel(), CreateArguments(methodInfo));
|
||||
var actionResult = await actionResultTask;
|
||||
var contentResult = Assert.IsType<ContentResult>(actionResult);
|
||||
Assert.Equal("Hello 0", contentResult.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForVoidReturningMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var model = new TestPageModel();
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(model, null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.Null(actionResult);
|
||||
Assert.True(model.SideEffects);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForVoidTaskReturningMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var model = new TestPageModel();
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidTaskReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(model, null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.Null(actionResult);
|
||||
Assert.True(model.SideEffects);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForTaskOfIActionResultReturningMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.GenericTaskHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPageModel(), null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<EmptyResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForTaskOfConcreteActionResultReturningMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.TaskReturningConcreteSubtype));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPageModel(), CreateArguments(methodInfo));
|
||||
var actionResult = await actionResultTask;
|
||||
var contentResult = Assert.IsType<ContentResult>(actionResult);
|
||||
Assert.Equal("value", contentResult.Content);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(TestPageModel.StringResult))]
|
||||
[InlineData(nameof(TestPageModel.TaskOfObject))]
|
||||
|
|
@ -322,16 +173,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
public void CreateExecutor_ThrowsIfTypeIsNotAValidReturnType(string methodName)
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(methodName);
|
||||
var handler = new HandlerMethodDescriptor()
|
||||
{
|
||||
MethodInfo = methodInfo,
|
||||
Parameters = CreateParameters(methodInfo),
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]));
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => ExecutorFactory.CreateExecutor(handler));
|
||||
Assert.Equal($"Unsupported handler method return type '{methodInfo.ReturnType}'.", ex.Message);
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +215,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
|||
{
|
||||
BindingInfo = BindingInfo.GetBindingInfo(p.GetCustomAttributes()),
|
||||
Name = p.Name,
|
||||
Parameter = p,
|
||||
ParameterInfo = p,
|
||||
ParameterType = p.ParameterType,
|
||||
}).ToArray();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,27 +36,34 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
Func<PageContext, object> factory = _ => null;
|
||||
Action<PageContext, object> releaser = (_, __) => { };
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
loader
|
||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
|
||||
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||
pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
pageFactoryProvider
|
||||
.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns(factory);
|
||||
pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
pageFactoryProvider
|
||||
.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns(releaser);
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
CreateActionDescriptorCollection(descriptor),
|
||||
pageFactoryProvider.Object);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
|
||||
var context = new ActionInvokerProviderContext(new ActionContext()
|
||||
{
|
||||
ActionDescriptor = descriptor,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
RouteData = new RouteData(),
|
||||
});
|
||||
|
||||
// Act
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
|
@ -65,106 +72,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.NotNull(context.Result);
|
||||
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||
var entry = actionInvoker.CacheEntry;
|
||||
var compiledPageActionDescriptor = Assert.IsType<CompiledPageActionDescriptor>(entry.ActionDescriptor);
|
||||
Assert.Equal(descriptor.RelativePath, compiledPageActionDescriptor.RelativePath);
|
||||
Assert.Equal(descriptor.RelativePath, entry.ActionDescriptor.RelativePath);
|
||||
Assert.Same(factory, entry.PageFactory);
|
||||
Assert.Same(releaser, entry.ReleasePage);
|
||||
Assert.Null(entry.ModelFactory);
|
||||
Assert.Null(entry.ReleaseModel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_CachesModelBinderFactory()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(PageWithBoundProperties).GetTypeInfo(),
|
||||
});
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
var pageFactoryProvider = Mock.Of<IPageFactoryProvider>();
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
pageFactoryProvider);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
|
||||
// Act
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.Result);
|
||||
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||
var entry = actionInvoker.CacheEntry;
|
||||
Assert.NotNull(entry.PropertyBinder);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_SetsHandlers()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
Func<PageContext, object> factory = _ => null;
|
||||
Action<PageContext, object> releaser = (_, __) => { };
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestSetPageWithModel)));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||
pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns(factory);
|
||||
pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns(releaser);
|
||||
|
||||
var modelFactoryProvider = new Mock<IPageModelFactoryProvider>();
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
pageFactoryProvider.Object,
|
||||
modelFactoryProvider.Object);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
|
||||
// Act
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.Result);
|
||||
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||
var entry = actionInvoker.CacheEntry;
|
||||
|
||||
Assert.Collection(entry.ActionDescriptor.HandlerMethods,
|
||||
handlerDescriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(TestSetPageModel.OnGet), handlerDescriptor.Method.Name);
|
||||
Assert.NotNull(handlerDescriptor.Executor);
|
||||
},
|
||||
handlerDescriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(TestSetPageModel.OnPost), handlerDescriptor.Method.Name);
|
||||
Assert.NotNull(handlerDescriptor.Executor);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_WithModel_PopulatesCacheEntry()
|
||||
{
|
||||
|
|
@ -174,36 +88,45 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
Func<PageContext, object> factory = _ => null;
|
||||
Action<PageContext, object> releaser = (_, __) => { };
|
||||
Func<PageContext, object> modelFactory = _ => null;
|
||||
Action<PageContext, object> modelDisposer = (_, __) => { };
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
loader
|
||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel)));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
|
||||
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||
pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
pageFactoryProvider
|
||||
.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns(factory);
|
||||
pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
pageFactoryProvider
|
||||
.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns(releaser);
|
||||
|
||||
var modelFactoryProvider = new Mock<IPageModelFactoryProvider>();
|
||||
modelFactoryProvider.Setup(f => f.CreateModelFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
modelFactoryProvider
|
||||
.Setup(f => f.CreateModelFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns(modelFactory);
|
||||
modelFactoryProvider.Setup(f => f.CreateModelDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
modelFactoryProvider
|
||||
.Setup(f => f.CreateModelDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns(modelDisposer);
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
CreateActionDescriptorCollection(descriptor),
|
||||
pageFactoryProvider.Object,
|
||||
modelFactoryProvider.Object);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
|
||||
var context = new ActionInvokerProviderContext(new ActionContext()
|
||||
{
|
||||
ActionDescriptor = descriptor,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
RouteData = new RouteData(),
|
||||
});
|
||||
|
||||
// Act
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
|
@ -232,18 +155,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
};
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
loader
|
||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel)));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
|
||||
var razorPageFactoryProvider = new Mock<IRazorPageFactoryProvider>();
|
||||
|
||||
Func<IRazorPage> factory1 = () => null;
|
||||
Func<IRazorPage> factory2 = () => null;
|
||||
|
||||
razorPageFactoryProvider
|
||||
.Setup(f => f.CreateFactory("/Home/Path1/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(factory1, new IChangeToken[0]));
|
||||
razorPageFactoryProvider.Setup(f => f.CreateFactory("/_ViewStart.cshtml"))
|
||||
razorPageFactoryProvider
|
||||
.Setup(f => f.CreateFactory("/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(factory2, new[] { Mock.Of<IChangeToken>() }));
|
||||
|
||||
var fileProvider = new TestFileProvider();
|
||||
|
|
@ -254,11 +179,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
CreateActionDescriptorCollection(descriptor),
|
||||
razorPageFactoryProvider: razorPageFactoryProvider.Object,
|
||||
razorProject: defaultRazorProject);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
|
||||
var context = new ActionInvokerProviderContext(new ActionContext()
|
||||
{
|
||||
ActionDescriptor = descriptor,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
RouteData = new RouteData(),
|
||||
});
|
||||
|
||||
// Act
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
|
@ -270,49 +200,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Equal(new[] { factory2, factory1 }, entry.ViewStartFactories);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_CachesExecutor()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor
|
||||
{
|
||||
RelativePath = "/Home/Path1/File.cshtml",
|
||||
ViewEnginePath = "/Home/Path1/File.cshtml",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel)));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
var razorPageFactoryProvider = new Mock<IRazorPageFactoryProvider>();
|
||||
var fileProvider = new TestFileProvider();
|
||||
var defaultRazorProject = new TestRazorProject(fileProvider);
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
razorPageFactoryProvider: razorPageFactoryProvider.Object,
|
||||
razorProject: defaultRazorProject);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
|
||||
// Act
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.Result);
|
||||
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||
var actionDescriptor = actionInvoker.CacheEntry.ActionDescriptor;
|
||||
Assert.Collection(actionDescriptor.HandlerMethods,
|
||||
handlerDescriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(TestPageModel.OnGet), handlerDescriptor.Method.Name);
|
||||
Assert.NotNull(handlerDescriptor.Executor);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_CachesEntries()
|
||||
|
|
@ -323,18 +210,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
loader
|
||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
CreateActionDescriptorCollection(descriptor));
|
||||
|
||||
var context = new ActionInvokerProviderContext(new ActionContext()
|
||||
{
|
||||
ActionDescriptor = descriptor,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
RouteData = new RouteData(),
|
||||
});
|
||||
|
||||
// Act - 1
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
|
@ -363,21 +254,31 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
var descriptorCollection1 = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var descriptorCollection2 = new ActionDescriptorCollection(new[] { descriptor }, version: 2);
|
||||
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.SetupSequence(p => p.ActionDescriptors)
|
||||
actionDescriptorProvider
|
||||
.SetupSequence(p => p.ActionDescriptors)
|
||||
.Returns(descriptorCollection1)
|
||||
.Returns(descriptorCollection2);
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
loader
|
||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
|
||||
var context = new ActionInvokerProviderContext(new ActionContext()
|
||||
{
|
||||
ActionDescriptor = descriptor,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
RouteData = new RouteData(),
|
||||
});
|
||||
|
||||
// Act - 1
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
|
@ -397,217 +298,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.NotSame(entry1, entry2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_DiscoversHandlersFromBaseType()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||
};
|
||||
|
||||
var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(InheritsMethods));
|
||||
|
||||
var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(actionDescriptor.HandlerMethods,
|
||||
(handler) =>
|
||||
{
|
||||
Assert.Equal("OnGet", handler.Method.Name);
|
||||
Assert.Equal(typeof(InheritsMethods), handler.Method.DeclaringType);
|
||||
},
|
||||
(handler) =>
|
||||
{
|
||||
Assert.Equal("OnGet", handler.Method.Name);
|
||||
Assert.Equal(typeof(TestSetPageModel), handler.Method.DeclaringType);
|
||||
},
|
||||
(handler) =>
|
||||
{
|
||||
Assert.Equal("OnPost", handler.Method.Name);
|
||||
Assert.Equal(typeof(TestSetPageModel), handler.Method.DeclaringType);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_IgnoresNonPublicMethods()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||
};
|
||||
|
||||
var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(ProtectedModel));
|
||||
|
||||
var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(actionDescriptor.HandlerMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_IgnoreGenericTypeParameters()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||
};
|
||||
|
||||
var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(GenericClassModel));
|
||||
|
||||
var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(actionDescriptor.HandlerMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_IgnoresStaticMethods()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Index.cshtml"
|
||||
};
|
||||
|
||||
var modelTypeInfo = typeof(PageModelWithStaticHandler).GetTypeInfo();
|
||||
var expected = modelTypeInfo.GetMethod(nameof(PageModelWithStaticHandler.OnGet), BindingFlags.Public | BindingFlags.Instance);
|
||||
var actionDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
ModelTypeInfo = modelTypeInfo,
|
||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||
};
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(modelTypeInfo, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(actionDescriptor.HandlerMethods,
|
||||
handler => Assert.Same(expected, handler.Method));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_IgnoresAbstractMethods()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Index.cshtml"
|
||||
};
|
||||
|
||||
var modelTypeInfo = typeof(PageModelWithAbstractMethod).GetTypeInfo();
|
||||
var expected = modelTypeInfo.GetMethod(nameof(PageModelWithAbstractMethod.OnGet));
|
||||
var actionDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
ModelTypeInfo = modelTypeInfo,
|
||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||
};
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(modelTypeInfo, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(actionDescriptor.HandlerMethods,
|
||||
handler => Assert.Same(expected, handler.Method));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_DiscoversMethodsWithFormActions()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Index.cshtml"
|
||||
};
|
||||
|
||||
var modelTypeInfo = typeof(PageModelWithFormActions).GetTypeInfo();
|
||||
var actionDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
ModelTypeInfo = modelTypeInfo,
|
||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||
};
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(modelTypeInfo, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(actionDescriptor.HandlerMethods.OrderBy(h => h.Method.Name),
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(modelTypeInfo.GetMethod(nameof(PageModelWithFormActions.OnGet)), handler.Method);
|
||||
Assert.Equal("GET", handler.HttpMethod);
|
||||
Assert.Equal(0, handler.FormAction.Length);
|
||||
Assert.NotNull(handler.Executor);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(modelTypeInfo.GetMethod(nameof(PageModelWithFormActions.OnPostAdd)), handler.Method);
|
||||
Assert.Equal("POST", handler.HttpMethod);
|
||||
Assert.Equal("Add", handler.FormAction.ToString());
|
||||
Assert.NotNull(handler.Executor);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(modelTypeInfo.GetMethod(nameof(PageModelWithFormActions.OnPostAddCustomer)), handler.Method);
|
||||
Assert.Equal("POST", handler.HttpMethod);
|
||||
Assert.Equal("AddCustomer", handler.FormAction.ToString());
|
||||
Assert.NotNull(handler.Executor);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(modelTypeInfo.GetMethod(nameof(PageModelWithFormActions.OnPostDeleteAsync)), handler.Method);
|
||||
Assert.Equal("POST", handler.HttpMethod);
|
||||
Assert.Equal("Delete", handler.FormAction.ToString());
|
||||
Assert.NotNull(handler.Executor);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_AllowOnlyOneMethod()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||
};
|
||||
|
||||
var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel));
|
||||
|
||||
var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
var handler = Assert.Single(actionDescriptor.HandlerMethods);
|
||||
Assert.Equal("OnGet", handler.Method.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetViewStartFactories_FindsFullHeirarchy()
|
||||
{
|
||||
|
|
@ -618,12 +308,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||
};
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
loader
|
||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel)));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
|
||||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile("/View/Deeper/Not_ViewStart.cshtml", "page content");
|
||||
|
|
@ -635,20 +324,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var razorProject = new TestRazorProject(fileProvider);
|
||||
|
||||
var mock = new Mock<IRazorPageFactoryProvider>();
|
||||
mock.Setup(p => p.CreateFactory("/Views/Deeper/_ViewStart.cshtml"))
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/Views/Deeper/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Verifiable();
|
||||
mock.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Verifiable();
|
||||
mock.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Verifiable();
|
||||
|
||||
var razorPageFactoryProvider = mock.Object;
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
CreateActionDescriptorCollection(descriptor),
|
||||
pageProvider: null,
|
||||
modelProvider: null,
|
||||
razorPageFactoryProvider: razorPageFactoryProvider,
|
||||
|
|
@ -675,16 +368,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Pages/Level1/Level2/Index.cshtml"
|
||||
};
|
||||
|
||||
var compiledPageDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||
};
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
loader
|
||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(compiledPageDescriptor);
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
|
||||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile("/_ViewStart.cshtml", "page content");
|
||||
|
|
@ -696,13 +389,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var razorProject = new TestRazorProject(fileProvider);
|
||||
|
||||
var mock = new Mock<IRazorPageFactoryProvider>(MockBehavior.Strict);
|
||||
mock.Setup(p => p.CreateFactory("/Pages/Level1/Level2/_ViewStart.cshtml"))
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/Pages/Level1/Level2/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Verifiable();
|
||||
mock.Setup(p => p.CreateFactory("/Pages/Level1/_ViewStart.cshtml"))
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/Pages/Level1/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Verifiable();
|
||||
var razorPageFactoryProvider = mock.Object;
|
||||
|
||||
var options = new RazorPagesOptions
|
||||
{
|
||||
RootDirectory = rootDirectory,
|
||||
|
|
@ -710,7 +406,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
CreateActionDescriptorCollection(descriptor),
|
||||
razorPageFactoryProvider: razorPageFactoryProvider,
|
||||
razorProject: razorProject,
|
||||
razorPagesOptions: options);
|
||||
|
|
@ -734,19 +430,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||
};
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
loader
|
||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel)));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory.Setup(f => f.CreateFactory("/Views/Deeper/_ViewStart.cshtml"))
|
||||
pageFactory
|
||||
.Setup(f => f.CreateFactory("/Views/Deeper/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new IChangeToken[0]));
|
||||
pageFactory.Setup(f => f.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
pageFactory
|
||||
.Setup(f => f.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(new IChangeToken[0]));
|
||||
pageFactory.Setup(f => f.CreateFactory("/_ViewStart.cshtml"))
|
||||
pageFactory
|
||||
.Setup(f => f.CreateFactory("/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new IChangeToken[0]));
|
||||
|
||||
// No files
|
||||
|
|
@ -755,7 +453,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
CreateActionDescriptorCollection(descriptor),
|
||||
pageProvider: null,
|
||||
modelProvider: null,
|
||||
razorPageFactoryProvider: pageFactory.Object,
|
||||
|
|
@ -770,14 +468,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Equal(2, factories.Count);
|
||||
}
|
||||
|
||||
private IRazorPageFactoryProvider CreateRazorPageFactoryProvider()
|
||||
{
|
||||
var mock = new Mock<IRazorPageFactoryProvider>();
|
||||
mock.Setup(p => p.CreateFactory(It.IsAny<string>()))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()));
|
||||
return mock.Object;
|
||||
}
|
||||
|
||||
private static CompiledPageActionDescriptor CreateCompiledPageActionDescriptor(
|
||||
PageActionDescriptor descriptor,
|
||||
Type pageType = null)
|
||||
|
|
@ -805,7 +495,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
RazorPagesOptions razorPagesOptions = null)
|
||||
{
|
||||
var tempDataFactory = new Mock<ITempDataDictionaryFactory>();
|
||||
tempDataFactory.Setup(t => t.GetTempData(It.IsAny<HttpContext>()))
|
||||
tempDataFactory
|
||||
.Setup(t => t.GetTempData(It.IsAny<HttpContext>()))
|
||||
.Returns((HttpContext context) => new TempDataDictionary(context, Mock.Of<ITempDataProvider>()));
|
||||
|
||||
if (razorProject == null)
|
||||
|
|
@ -838,102 +529,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
private class GenericClassModel
|
||||
private IActionDescriptorCollectionProvider CreateActionDescriptorCollection(PageActionDescriptor descriptor)
|
||||
{
|
||||
public void OnGet<T>()
|
||||
{
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider
|
||||
.Setup(p => p.ActionDescriptors)
|
||||
.Returns(descriptorCollection);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSetPageWithModel
|
||||
{
|
||||
public TestSetPageModel Model { get; set; }
|
||||
}
|
||||
|
||||
private class InheritsMethods : TestSetPageModel
|
||||
{
|
||||
public new void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class PageModelWithStaticHandler
|
||||
{
|
||||
public static void OnGet(string name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class PageModelWithAbstractMethod
|
||||
{
|
||||
public abstract void OnPost(string name);
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class PageModelWithFormActions
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPostAdd()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPostAddCustomer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPostDeleteAsync()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void OnPostDelete()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class ProtectedModel
|
||||
{
|
||||
protected void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void OnPost()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSetPageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPost()
|
||||
{
|
||||
|
||||
}
|
||||
return actionDescriptorProvider.Object;
|
||||
}
|
||||
|
||||
private class PageWithModel
|
||||
|
|
@ -947,11 +551,5 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class PageWithBoundProperties
|
||||
{
|
||||
[ModelBinder]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,6 +353,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new FilterItem[0]);
|
||||
var invoker = CreateInvoker(
|
||||
new[] { filter1.Object, filter2.Object, filter3.Object },
|
||||
|
|
@ -408,6 +409,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new FilterItem[0]);
|
||||
var invoker = CreateInvoker(
|
||||
new IFilterMetadata[] { filter1.Object, filter2.Object, filter3.Object },
|
||||
|
|
@ -521,6 +523,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
ViewEnginePath = "/Index.cshtml",
|
||||
RelativePath = "/Index.cshtml",
|
||||
HandlerTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
|
||||
|
|
@ -607,6 +611,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
(c, model) => { (model as IDisposable)?.Dispose(); },
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new FilterItem[0]);
|
||||
|
||||
var invoker = new PageActionInvoker(
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -149,20 +151,51 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
public async Task ModelBinderFactory_BindsPropertiesOnPage()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageWithProperty).GetTypeInfo();
|
||||
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
||||
BoundProperties = new []
|
||||
{
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageWithProperty.Id),
|
||||
ParameterType = typeof(int),
|
||||
Property = type.GetProperty(nameof(PageWithProperty.Id)),
|
||||
},
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageWithProperty.RouteDifferentValue),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageWithProperty.RouteDifferentValue)),
|
||||
},
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageWithProperty.PropertyWithNoValue),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageWithProperty.PropertyWithNoValue)),
|
||||
}
|
||||
},
|
||||
HandlerTypeInfo = type,
|
||||
PageTypeInfo = type,
|
||||
};
|
||||
|
||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
||||
var binder = new TestParameterBinder(new Dictionary<string, object>
|
||||
{
|
||||
{ nameof(PageWithProperty.Id), 10 },
|
||||
{ nameof(PageWithProperty.RouteDifferentValue), "route-value" }
|
||||
});
|
||||
|
||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||
|
||||
var page = new PageWithProperty
|
||||
{
|
||||
PageContext = new PageContext(),
|
||||
PageContext = new PageContext()
|
||||
{
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -172,56 +205,60 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Equal(10, page.Id);
|
||||
Assert.Equal("route-value", page.RouteDifferentValue);
|
||||
Assert.Null(page.PropertyWithNoValue);
|
||||
Assert.Collection(binder.Descriptors,
|
||||
descriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(PageWithProperty.Id), descriptor.Name);
|
||||
Assert.Null(descriptor.BindingInfo.BinderModelName);
|
||||
Assert.Equal(BindingSource.Query, descriptor.BindingInfo.BindingSource);
|
||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
||||
Assert.Null(descriptor.BindingInfo.PropertyFilterProvider);
|
||||
Assert.Equal(typeof(int), descriptor.ParameterType);
|
||||
},
|
||||
descriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(PageWithProperty.RouteDifferentValue), descriptor.Name);
|
||||
Assert.Equal("route-value", descriptor.BindingInfo.BinderModelName);
|
||||
Assert.Equal(BindingSource.Path, descriptor.BindingInfo.BindingSource);
|
||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
||||
Assert.Null(descriptor.BindingInfo.PropertyFilterProvider);
|
||||
Assert.Equal(typeof(string), descriptor.ParameterType);
|
||||
},
|
||||
descriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(PageWithProperty.PropertyWithNoValue), descriptor.Name);
|
||||
Assert.Null(descriptor.BindingInfo.BinderModelName);
|
||||
Assert.Equal(BindingSource.Form, descriptor.BindingInfo.BindingSource);
|
||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
||||
Assert.Null(descriptor.BindingInfo.PropertyFilterProvider);
|
||||
Assert.Equal(typeof(string), descriptor.ParameterType);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ModelBinderFactory_BindsPropertiesOnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageModelWithProperty).GetTypeInfo();
|
||||
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
BoundProperties = new[]
|
||||
{
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageModelWithProperty.Id),
|
||||
ParameterType = typeof(int),
|
||||
Property = type.GetProperty(nameof(PageModelWithProperty.Id)),
|
||||
},
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageModelWithProperty.RouteDifferentValue),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageModelWithProperty.RouteDifferentValue)),
|
||||
},
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageModelWithProperty.PropertyWithNoValue),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageModelWithProperty.PropertyWithNoValue)),
|
||||
}
|
||||
},
|
||||
|
||||
HandlerTypeInfo = typeof(PageModelWithProperty).GetTypeInfo(),
|
||||
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModelWithProperty).GetTypeInfo(),
|
||||
};
|
||||
|
||||
var binder = new TestParameterBinder(new Dictionary<string, object>
|
||||
{
|
||||
{ nameof(PageModelWithProperty.Id), 10 },
|
||||
{ nameof(PageModelWithProperty.RouteDifferentValue), "route-value" }
|
||||
});
|
||||
|
||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||
|
||||
var page = new PageWithProperty
|
||||
{
|
||||
PageContext = new PageContext(),
|
||||
PageContext = new PageContext()
|
||||
{
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
}
|
||||
};
|
||||
|
||||
var model = new PageModelWithProperty();
|
||||
|
||||
// Act
|
||||
|
|
@ -235,123 +272,44 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Equal(10, model.Id);
|
||||
Assert.Equal("route-value", model.RouteDifferentValue);
|
||||
Assert.Null(model.PropertyWithNoValue);
|
||||
}
|
||||
|
||||
Assert.Collection(binder.Descriptors,
|
||||
descriptor =>
|
||||
[Fact]
|
||||
public async Task ModelBinderFactory_PreservesExistingValueIfModelBindingFailed()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageModelWithDefaultValue).GetTypeInfo();
|
||||
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
BoundProperties = new[]
|
||||
{
|
||||
Assert.Equal(nameof(PageModelWithProperty.Id), descriptor.Name);
|
||||
Assert.Equal(BindingSource.Query, descriptor.BindingInfo.BindingSource);
|
||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
||||
Assert.Null(descriptor.BindingInfo.PropertyFilterProvider);
|
||||
Assert.Equal(typeof(int), descriptor.ParameterType);
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageModelWithDefaultValue.PropertyWithDefaultValue),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageModelWithDefaultValue.PropertyWithDefaultValue)),
|
||||
},
|
||||
},
|
||||
descriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(PageModelWithProperty.RouteDifferentValue), descriptor.Name);
|
||||
Assert.Equal("route-value", descriptor.BindingInfo.BinderModelName);
|
||||
Assert.Equal(BindingSource.Path, descriptor.BindingInfo.BindingSource);
|
||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
||||
Assert.Null(descriptor.BindingInfo.PropertyFilterProvider);
|
||||
Assert.Equal(typeof(string), descriptor.ParameterType);
|
||||
},
|
||||
descriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(PageModelWithProperty.PropertyWithNoValue), descriptor.Name);
|
||||
Assert.Null(descriptor.BindingInfo.BinderModelName);
|
||||
Assert.Equal(BindingSource.Form, descriptor.BindingInfo.BindingSource);
|
||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
||||
Assert.Null(descriptor.BindingInfo.PropertyFilterProvider);
|
||||
Assert.Equal(typeof(string), descriptor.ParameterType);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ModelBinderFactory_DiscoversBinderType()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerTypeInfo = type,
|
||||
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModelWithModelBinderAttribute).GetTypeInfo(),
|
||||
ModelTypeInfo = type,
|
||||
};
|
||||
var expected = Guid.NewGuid();
|
||||
var binder = new TestParameterBinder(new Dictionary<string, object>
|
||||
{
|
||||
{ nameof(PageModelWithModelBinderAttribute.PropertyWithBinderType), expected },
|
||||
});
|
||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||
var page = new PageWithProperty
|
||||
{
|
||||
PageContext = new PageContext(),
|
||||
};
|
||||
var model = new PageModelWithModelBinderAttribute();
|
||||
|
||||
// Act
|
||||
await factory(page, model);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, model.PropertyWithBinderType);
|
||||
Assert.Collection(binder.Descriptors,
|
||||
descriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(PageModelWithModelBinderAttribute.PropertyWithBinderType), descriptor.Name);
|
||||
Assert.Equal(BindingSource.Custom, descriptor.BindingInfo.BindingSource);
|
||||
Assert.Equal(typeof(DeclarativeSecurityAction), descriptor.BindingInfo.BinderType);
|
||||
Assert.Null(descriptor.BindingInfo.PropertyFilterProvider);
|
||||
Assert.Equal(typeof(Guid), descriptor.ParameterType);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ModelBinderFactory_DiscoversPropertyFilter()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModelWithPropertyFilterAttribute).GetTypeInfo(),
|
||||
};
|
||||
var binder = new TestParameterBinder(new Dictionary<string, object>());
|
||||
|
||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||
|
||||
var page = new PageWithProperty
|
||||
{
|
||||
PageContext = new PageContext(),
|
||||
};
|
||||
var model = new PageModelWithPropertyFilterAttribute();
|
||||
|
||||
// Act
|
||||
await factory(page, model);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(binder.Descriptors,
|
||||
descriptor =>
|
||||
PageContext = new PageContext()
|
||||
{
|
||||
Assert.Equal(nameof(PageModelWithPropertyFilterAttribute.PropertyWithFilter), descriptor.Name);
|
||||
Assert.Null(descriptor.BindingInfo.BindingSource);
|
||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
||||
Assert.IsType<TestPropertyFilterProvider>(descriptor.BindingInfo.PropertyFilterProvider);
|
||||
Assert.Equal(typeof(object), descriptor.ParameterType);
|
||||
});
|
||||
}
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
}
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public async Task ModelBinderFactory_UsesDefaultValueIfModelBindingFailed()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModelWithDefaultValue).GetTypeInfo(),
|
||||
};
|
||||
var binder = new TestParameterBinder(new Dictionary<string, object>());
|
||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||
var page = new PageWithProperty
|
||||
{
|
||||
PageContext = new PageContext(),
|
||||
};
|
||||
var model = new PageModelWithDefaultValue();
|
||||
var defaultValue = model.PropertyWithDefaultValue;
|
||||
|
||||
|
|
@ -362,34 +320,125 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Equal(defaultValue, model.PropertyWithDefaultValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ModelBinderFactory_OverwritesDefaultValue()
|
||||
[Theory]
|
||||
[InlineData("Get")]
|
||||
[InlineData("GET")]
|
||||
[InlineData("gET")]
|
||||
public async Task ModelBinderFactory_IgnoresPropertyWithoutSupportsGet_WhenRequestIsGet(string method)
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageModelWithSupportsGetProperty).GetTypeInfo();
|
||||
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
BoundProperties = new[]
|
||||
{
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageModelWithSupportsGetProperty.SupportsGet),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageModelWithSupportsGetProperty.SupportsGet)),
|
||||
SupportsGet = true,
|
||||
},
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageModelWithSupportsGetProperty.Default),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageModelWithSupportsGetProperty.Default)),
|
||||
},
|
||||
},
|
||||
|
||||
HandlerTypeInfo = type,
|
||||
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModelWithDefaultValue).GetTypeInfo(),
|
||||
ModelTypeInfo = type,
|
||||
};
|
||||
var expected = "not-default-value";
|
||||
var binder = new TestParameterBinder(new Dictionary<string, object>
|
||||
|
||||
var binder = new TestParameterBinder(new Dictionary<string, object>()
|
||||
{
|
||||
{ nameof(PageModelWithDefaultValue.PropertyWithDefaultValue), expected },
|
||||
{ "SupportsGet", "value" },
|
||||
{ "Default", "ignored" },
|
||||
});
|
||||
|
||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||
|
||||
var page = new PageWithProperty
|
||||
{
|
||||
PageContext = new PageContext(),
|
||||
PageContext = new PageContext()
|
||||
{
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
}
|
||||
};
|
||||
var model = new PageModelWithDefaultValue();
|
||||
var defaultValue = model.PropertyWithDefaultValue;
|
||||
|
||||
page.HttpContext.Request.Method = method;
|
||||
|
||||
var model = new PageModelWithSupportsGetProperty();
|
||||
|
||||
// Act
|
||||
await factory(page, model);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, model.PropertyWithDefaultValue);
|
||||
Assert.Equal("value", model.SupportsGet);
|
||||
Assert.Null(model.Default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ModelBinderFactory_BindsPropertyWithoutSupportsGet_WhenRequestIsNotGet()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PageModelWithSupportsGetProperty).GetTypeInfo();
|
||||
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
BoundProperties = new[]
|
||||
{
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageModelWithSupportsGetProperty.SupportsGet),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageModelWithSupportsGetProperty.SupportsGet)),
|
||||
SupportsGet = true,
|
||||
},
|
||||
new PageBoundPropertyDescriptor()
|
||||
{
|
||||
Name = nameof(PageModelWithSupportsGetProperty.Default),
|
||||
ParameterType = typeof(string),
|
||||
Property = type.GetProperty(nameof(PageModelWithSupportsGetProperty.Default)),
|
||||
},
|
||||
},
|
||||
|
||||
HandlerTypeInfo = type,
|
||||
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
||||
ModelTypeInfo = type,
|
||||
};
|
||||
|
||||
var binder = new TestParameterBinder(new Dictionary<string, object>()
|
||||
{
|
||||
{ "SupportsGet", "value" },
|
||||
{ "Default", "value" },
|
||||
});
|
||||
|
||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||
|
||||
var page = new PageWithProperty
|
||||
{
|
||||
PageContext = new PageContext()
|
||||
{
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
}
|
||||
};
|
||||
|
||||
page.HttpContext.Request.Method = "Post";
|
||||
|
||||
var model = new PageModelWithSupportsGetProperty();
|
||||
|
||||
// Act
|
||||
await factory(page, model);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("value", model.SupportsGet);
|
||||
Assert.Equal("value", model.Default);
|
||||
}
|
||||
|
||||
private class TestParameterBinder : ParameterBinder
|
||||
|
|
@ -518,5 +567,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
[ModelBinder]
|
||||
public string PropertyWithDefaultValue { get; set; } = "Hello world";
|
||||
}
|
||||
|
||||
private class PageModelWithSupportsGetProperty
|
||||
{
|
||||
[BindProperty(SupportsGet = true)]
|
||||
public string SupportsGet { get; set; }
|
||||
|
||||
public string Default { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
@page
|
||||
@using Microsoft.AspNetCore.Mvc.RazorPages
|
||||
|
||||
@functions
|
||||
{
|
||||
[BindProperty(SupportsGet=true)]
|
||||
public int Value { get; set; }
|
||||
}
|
||||
<p>@Value</p>
|
||||
|
|
@ -13,5 +13,7 @@ namespace RazorPagesWebSite
|
|||
|
||||
[FromRoute]
|
||||
public int Id { get; set; }
|
||||
|
||||
public void OnGet() { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue