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>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="TypeInfo"/> of the page.
|
/// Gets the list of handler methods for the page.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="TypeInfo"/> of the model.
|
/// Gets or sets the <see cref="TypeInfo"/> of the model.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TypeInfo ModelTypeInfo { get; set; }
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
public class HandlerMethodDescriptor
|
public class HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
public MethodInfo Method { get; set; }
|
public MethodInfo MethodInfo { get; set; }
|
||||||
|
|
||||||
public Func<object, object[], Task<IActionResult>> Executor { get; set; }
|
|
||||||
|
|
||||||
public string HttpMethod { get; set; }
|
public string HttpMethod { get; set; }
|
||||||
|
|
||||||
public StringSegment FormAction { get; set; }
|
public string FormAction { get; set; }
|
||||||
|
|
||||||
public HandlerParameterDescriptor[] Parameters { get; set; }
|
public IList<HandlerParameterDescriptor> Parameters { get; set; }
|
||||||
|
|
||||||
public bool OnPage { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
|
||||||
|
|
@ -9,8 +8,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
public class HandlerParameterDescriptor : ParameterDescriptor
|
public class HandlerParameterDescriptor : ParameterDescriptor
|
||||||
{
|
{
|
||||||
public object DefaultValue { get; set; }
|
public ParameterInfo ParameterInfo { get; set; }
|
||||||
|
|
||||||
public ParameterInfo Parameter { 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)
|
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));
|
throw new InvalidOperationException(Resources.FormatAmbiguousHandler(Environment.NewLine, ambiguousMethods));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (handler.FormAction.HasValue &&
|
else if (handler.FormAction != null &&
|
||||||
!handler.FormAction.Equals(formAction, StringComparison.OrdinalIgnoreCase))
|
!handler.FormAction.Equals(formAction, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -20,21 +24,249 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
public CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor)
|
public CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor)
|
||||||
{
|
{
|
||||||
var compilationResult = _compiler.Compile(actionDescriptor.RelativePath);
|
var result = _compiler.Compile(actionDescriptor.RelativePath);
|
||||||
var compiledTypeInfo = compilationResult.CompiledType.GetTypeInfo();
|
return CreateDescriptor(actionDescriptor, result.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();
|
// Internal for unit testing
|
||||||
if (modelTypeInfo == compiledTypeInfo)
|
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)
|
return new CompiledPageActionDescriptor(actionDescriptor)
|
||||||
{
|
{
|
||||||
PageTypeInfo = compiledTypeInfo,
|
ActionConstraints = actionDescriptor.ActionConstraints,
|
||||||
ModelTypeInfo = modelTypeInfo,
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -12,38 +13,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
public static class ExecutorFactory
|
public static class ExecutorFactory
|
||||||
{
|
{
|
||||||
public static Func<object, object[], Task<IActionResult>> CreateExecutor(
|
public static Func<object, object[], Task<IActionResult>> CreateExecutor(HandlerMethodDescriptor handlerDescriptor)
|
||||||
CompiledPageActionDescriptor actionDescriptor,
|
|
||||||
MethodInfo method,
|
|
||||||
HandlerParameterDescriptor[] parameters)
|
|
||||||
{
|
{
|
||||||
if (actionDescriptor == null)
|
if (handlerDescriptor == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(actionDescriptor));
|
throw new ArgumentNullException(nameof(handlerDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var handler = CreateHandlerMethod(handlerDescriptor);
|
||||||
|
|
||||||
if (method == null)
|
return handler.Execute;
|
||||||
{
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 methodParameters = method.GetParameters();
|
||||||
|
|
||||||
var returnType = method.ReturnType;
|
var returnType = method.ReturnType;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.ExceptionServices;
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
|
@ -327,7 +328,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
_pageContext.ViewStarts = viewStarts;
|
_pageContext.ViewStarts = viewStarts;
|
||||||
|
|
||||||
if (actionDescriptor.ModelTypeInfo == null)
|
if (actionDescriptor.ModelTypeInfo == actionDescriptor.PageTypeInfo)
|
||||||
{
|
{
|
||||||
_model = _page;
|
_model = _page;
|
||||||
}
|
}
|
||||||
|
|
@ -341,10 +342,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
_pageContext.ViewData.Model = _model;
|
_pageContext.ViewData.Model = _model;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CacheEntry.PropertyBinder != null &&
|
if (CacheEntry.PropertyBinder != null)
|
||||||
!string.Equals(_pageContext.HttpContext.Request.Method, "GET", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
// Don't bind properties on GET requests
|
|
||||||
await CacheEntry.PropertyBinder(_page, _model);
|
await CacheEntry.PropertyBinder(_page, _model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,8 +378,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
var arguments = await GetArguments(handler);
|
var arguments = await GetArguments(handler);
|
||||||
|
|
||||||
var executor = handler.Executor;
|
Func<object, object[], Task<IActionResult>> executor = null;
|
||||||
result = await executor(handler.OnPage ? _page : _model, arguments);
|
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)
|
if (result == null)
|
||||||
|
|
@ -393,10 +402,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
private async Task<object[]> GetArguments(HandlerMethodDescriptor handler)
|
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);
|
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];
|
var parameter = handler.Parameters[i];
|
||||||
|
|
||||||
|
|
@ -406,7 +415,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
parameter,
|
parameter,
|
||||||
value: null);
|
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;
|
return arguments;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Razor;
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
@ -19,6 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Func<PageContext, object> modelFactory,
|
Func<PageContext, object> modelFactory,
|
||||||
Action<PageContext, object> releaseModel,
|
Action<PageContext, object> releaseModel,
|
||||||
Func<Page, object, Task> propertyBinder,
|
Func<Page, object, Task> propertyBinder,
|
||||||
|
Func<object, object[], Task<IActionResult>>[] executors,
|
||||||
IReadOnlyList<Func<IRazorPage>> viewStartFactories,
|
IReadOnlyList<Func<IRazorPage>> viewStartFactories,
|
||||||
FilterItem[] cacheableFilters)
|
FilterItem[] cacheableFilters)
|
||||||
{
|
{
|
||||||
|
|
@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
ModelFactory = modelFactory;
|
ModelFactory = modelFactory;
|
||||||
ReleaseModel = releaseModel;
|
ReleaseModel = releaseModel;
|
||||||
PropertyBinder = propertyBinder;
|
PropertyBinder = propertyBinder;
|
||||||
|
Executors = executors;
|
||||||
ViewStartFactories = viewStartFactories;
|
ViewStartFactories = viewStartFactories;
|
||||||
CacheableFilters = cacheableFilters;
|
CacheableFilters = cacheableFilters;
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +55,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<Page, object, Task> PropertyBinder { get; }
|
public Func<Page, object, Task> PropertyBinder { get; }
|
||||||
|
|
||||||
|
public Func<object, object[], Task<IActionResult>>[] Executors { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the applicable ViewStart pages.
|
/// Gets the applicable ViewStart pages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
|
@ -181,20 +182,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
Func<PageContext, object> modelFactory = null;
|
Func<PageContext, object> modelFactory = null;
|
||||||
Action<PageContext, object> modelReleaser = 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);
|
modelFactory = _modelFactoryProvider.CreateModelFactory(compiledActionDescriptor);
|
||||||
modelReleaser = _modelFactoryProvider.CreateModelDisposer(compiledActionDescriptor);
|
modelReleaser = _modelFactoryProvider.CreateModelDisposer(compiledActionDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
var viewStartFactories = GetViewStartFactories(compiledActionDescriptor);
|
var viewStartFactories = GetViewStartFactories(compiledActionDescriptor);
|
||||||
|
|
||||||
|
var executors = GetExecutors(compiledActionDescriptor);
|
||||||
|
|
||||||
return new PageActionInvokerCacheEntry(
|
return new PageActionInvokerCacheEntry(
|
||||||
compiledActionDescriptor,
|
compiledActionDescriptor,
|
||||||
pageFactory,
|
pageFactory,
|
||||||
|
|
@ -202,6 +199,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
modelFactory,
|
modelFactory,
|
||||||
modelReleaser,
|
modelReleaser,
|
||||||
propertyBinder,
|
propertyBinder,
|
||||||
|
executors,
|
||||||
viewStartFactories,
|
viewStartFactories,
|
||||||
cachedFilters);
|
cachedFilters);
|
||||||
}
|
}
|
||||||
|
|
@ -226,82 +224,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
return viewStartFactories;
|
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)
|
private static object GetDefaultValue(ParameterInfo methodParameter)
|
||||||
{
|
{
|
||||||
object defaultValue = null;
|
object defaultValue = null;
|
||||||
|
|
@ -317,42 +239,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
return defaultValue;
|
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
|
if (actionDescriptor.HandlerMethods == null || actionDescriptor.HandlerMethods.Count == 0)
|
||||||
// (such as property accessors and operator overloading methods).
|
|
||||||
if (methodInfo.IsSpecialName)
|
|
||||||
{
|
{
|
||||||
return false;
|
return Array.Empty<Func<object, object[], Task<IActionResult>>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overriden methods from Object class, e.g. Equals(Object), GetHashCode(), etc., are not valid.
|
var results = new Func<object, object[], Task<IActionResult>>[actionDescriptor.HandlerMethods.Count];
|
||||||
if (methodInfo.GetBaseDefinition().DeclaringType == typeof(object))
|
|
||||||
|
for (var i = 0; i < actionDescriptor.HandlerMethods.Count; i++)
|
||||||
{
|
{
|
||||||
return false;
|
results[i] = ExecutorFactory.CreateExecutor(actionDescriptor.HandlerMethods[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methodInfo.IsStatic)
|
return results;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodInfo.IsAbstract)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodInfo.IsConstructor)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodInfo.IsGenericMethod)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return methodInfo.IsPublic;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class InnerCache
|
internal class InnerCache
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,11 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -29,15 +28,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
throw new ArgumentNullException(nameof(actionDescriptor));
|
throw new ArgumentNullException(nameof(actionDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
var bindPropertiesOnPage = actionDescriptor.ModelTypeInfo == null;
|
var properties = actionDescriptor.BoundProperties;
|
||||||
var target = bindPropertiesOnPage ? actionDescriptor.PageTypeInfo : actionDescriptor.ModelTypeInfo;
|
if (properties == null || properties.Count == 0)
|
||||||
var propertiesToBind = GetPropertiesToBind(modelMetadataProvider, target);
|
|
||||||
|
|
||||||
if (propertiesToBind.Count == 0)
|
|
||||||
{
|
{
|
||||||
return null;
|
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;
|
return Bind;
|
||||||
|
|
||||||
Task Bind(Page page, object model)
|
Task Bind(Page page, object model)
|
||||||
|
|
@ -47,14 +52,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
throw new ArgumentNullException(nameof(page));
|
throw new ArgumentNullException(nameof(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bindPropertiesOnPage && model == null)
|
if (!isHandlerThePage && model == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(model));
|
throw new ArgumentNullException(nameof(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
var pageContext = page.PageContext;
|
var pageContext = page.PageContext;
|
||||||
var instance = bindPropertiesOnPage ? page : model;
|
var instance = isHandlerThePage ? page : model;
|
||||||
return BindPropertiesAsync(parameterBinder, pageContext, instance, propertiesToBind);
|
return BindPropertiesAsync(parameterBinder, pageContext, instance, properties, metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,92 +67,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
ParameterBinder parameterBinder,
|
ParameterBinder parameterBinder,
|
||||||
PageContext pageContext,
|
PageContext pageContext,
|
||||||
object instance,
|
object instance,
|
||||||
IList<PropertyBindingInfo> propertiesToBind)
|
IList<ParameterDescriptor> properties,
|
||||||
|
IList<ModelMetadata> metadata)
|
||||||
{
|
{
|
||||||
var valueProvider = await GetCompositeValueProvider(pageContext);
|
var isGet = string.Equals("GET", pageContext.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase);
|
||||||
for (var i = 0; i < propertiesToBind.Count; i++)
|
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(pageContext, pageContext.ValueProviderFactories);
|
||||||
|
for (var i = 0; i < properties.Count; i++)
|
||||||
{
|
{
|
||||||
var propertyBindingInfo = propertiesToBind[i];
|
if (isGet && !((PageBoundPropertyDescriptor)properties[i]).SupportsGet)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var propertyMetadata = typeMetadata.Properties[property.Name] ??
|
var result = await parameterBinder.BindModelAsync(pageContext, valueProvider, properties[i]);
|
||||||
modelMetadataProvider.GetMetadataForProperty(handlerType, property.Name);
|
if (result.IsModelSet)
|
||||||
if (propertyMetadata == null)
|
|
||||||
{
|
{
|
||||||
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>
|
/// <summary>
|
||||||
/// A base class for a Razor page.
|
/// A base class for a Razor page.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[PagesBaseClass]
|
||||||
public abstract class Page : RazorPageBase, IRazorPage
|
public abstract class Page : RazorPageBase, IRazorPage
|
||||||
{
|
{
|
||||||
private IObjectModelValidator _objectValidator;
|
private IObjectModelValidator _objectValidator;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
{
|
{
|
||||||
|
[PagesBaseClass]
|
||||||
public abstract class PageModel
|
public abstract class PageModel
|
||||||
{
|
{
|
||||||
private IObjectModelValidator _objectValidator;
|
private IObjectModelValidator _objectValidator;
|
||||||
|
|
|
||||||
|
|
@ -716,6 +716,22 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.DoesNotContain(validationError, content);
|
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]
|
[Fact]
|
||||||
public async Task PagePropertiesAreInjected()
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
@ -30,7 +32,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -67,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor,
|
descriptor,
|
||||||
},
|
},
|
||||||
|
|
@ -109,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -140,20 +142,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var descriptor1 = new HandlerMethodDescriptor
|
var descriptor1 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Add"),
|
FormAction = "Add",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor2 = new HandlerMethodDescriptor
|
var descriptor2 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Delete"),
|
FormAction = "Delete",
|
||||||
};
|
};
|
||||||
|
|
||||||
var pageContext = new PageContext
|
var pageContext = new PageContext
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -190,20 +192,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var descriptor1 = new HandlerMethodDescriptor
|
var descriptor1 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Add"),
|
FormAction = "Add",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor2 = new HandlerMethodDescriptor
|
var descriptor2 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Delete"),
|
FormAction = "Delete",
|
||||||
};
|
};
|
||||||
|
|
||||||
var pageContext = new PageContext
|
var pageContext = new PageContext
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -240,20 +242,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var descriptor1 = new HandlerMethodDescriptor
|
var descriptor1 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Add"),
|
FormAction = "Add",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor2 = new HandlerMethodDescriptor
|
var descriptor2 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Delete"),
|
FormAction = "Delete",
|
||||||
};
|
};
|
||||||
|
|
||||||
var pageContext = new PageContext
|
var pageContext = new PageContext
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -285,20 +287,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var descriptor1 = new HandlerMethodDescriptor
|
var descriptor1 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Add"),
|
FormAction = "Add",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor2 = new HandlerMethodDescriptor
|
var descriptor2 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Delete"),
|
FormAction = "Delete",
|
||||||
};
|
};
|
||||||
|
|
||||||
var pageContext = new PageContext
|
var pageContext = new PageContext
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -336,20 +338,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var descriptor1 = new HandlerMethodDescriptor
|
var descriptor1 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Add"),
|
FormAction = "Add",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor2 = new HandlerMethodDescriptor
|
var descriptor2 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Delete"),
|
FormAction = "Delete",
|
||||||
};
|
};
|
||||||
|
|
||||||
var pageContext = new PageContext
|
var pageContext = new PageContext
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -381,7 +383,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var descriptor1 = new HandlerMethodDescriptor
|
var descriptor1 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Subscribe"),
|
FormAction = "Subscribe",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor2 = new HandlerMethodDescriptor
|
var descriptor2 = new HandlerMethodDescriptor
|
||||||
|
|
@ -393,7 +395,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -429,13 +431,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptor1 = new HandlerMethodDescriptor
|
var descriptor1 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
Method = GetType().GetMethod(nameof(Post)),
|
MethodInfo = GetType().GetMethod(nameof(Post)),
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor2 = new HandlerMethodDescriptor
|
var descriptor2 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
Method = GetType().GetMethod(nameof(PostAsync)),
|
MethodInfo = GetType().GetMethod(nameof(PostAsync)),
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -448,7 +450,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -468,7 +470,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() => selector.Select(pageContext));
|
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:" +
|
var message = "Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:" +
|
||||||
Environment.NewLine + Environment.NewLine + methods;
|
Environment.NewLine + Environment.NewLine + methods;
|
||||||
|
|
||||||
|
|
@ -481,16 +483,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptor1 = new HandlerMethodDescriptor
|
var descriptor1 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
Method = GetType().GetMethod(nameof(Post)),
|
MethodInfo = GetType().GetMethod(nameof(Post)),
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Add"),
|
FormAction = "Add",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor2 = new HandlerMethodDescriptor
|
var descriptor2 = new HandlerMethodDescriptor
|
||||||
{
|
{
|
||||||
Method = GetType().GetMethod(nameof(PostAsync)),
|
MethodInfo = GetType().GetMethod(nameof(PostAsync)),
|
||||||
HttpMethod = "POST",
|
HttpMethod = "POST",
|
||||||
FormAction = new StringSegment("Add"),
|
FormAction = "Add",
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptor3 = new HandlerMethodDescriptor
|
var descriptor3 = new HandlerMethodDescriptor
|
||||||
|
|
@ -502,7 +504,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
HandlerMethods =
|
HandlerMethods = new List<HandlerMethodDescriptor>()
|
||||||
{
|
{
|
||||||
descriptor1,
|
descriptor1,
|
||||||
descriptor2,
|
descriptor2,
|
||||||
|
|
@ -528,7 +530,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() => selector.Select(pageContext));
|
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:" +
|
var message = "Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:" +
|
||||||
Environment.NewLine + Environment.NewLine + methods;
|
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
|
public class ExecutorFactoryTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateExecutor_ForActionResultMethod_OnPage()
|
public async Task CreateExecutor_ForActionResultMethod()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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
|
// Act
|
||||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(executor);
|
Assert.NotNull(executor);
|
||||||
|
|
@ -36,17 +36,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateExecutor_ForMethodReturningConcreteSubtypeOfIActionResult_OnPage()
|
public async Task CreateExecutor_ForMethodReturningConcreteSubtypeOfIActionResult()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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
|
// Act
|
||||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(executor);
|
Assert.NotNull(executor);
|
||||||
|
|
@ -56,18 +56,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateExecutor_ForActionResultReturningMethod_WithParameters_OnPage()
|
public async Task CreateExecutor_ForActionResultReturningMethod_WithParameters()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var actionDescriptor = new CompiledPageActionDescriptor
|
|
||||||
{
|
|
||||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
||||||
};
|
|
||||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturnHandlerWithParameters));
|
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturnHandlerWithParameters));
|
||||||
var parameters = CreateParameters(methodInfo);
|
var handler = new HandlerMethodDescriptor()
|
||||||
|
{
|
||||||
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = CreateParameters(methodInfo),
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, parameters);
|
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(executor);
|
Assert.NotNull(executor);
|
||||||
|
|
@ -78,18 +78,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateExecutor_ForVoidReturningMethod_OnPage()
|
public async Task CreateExecutor_ForVoidReturningMethod()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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 page = new TestPage();
|
||||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidReturningHandler));
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(executor);
|
Assert.NotNull(executor);
|
||||||
|
|
@ -100,18 +101,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateExecutor_ForVoidTaskReturningMethod_OnPage()
|
public async Task CreateExecutor_ForVoidTaskReturningMethod()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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 page = new TestPage();
|
||||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidTaskReturningHandler));
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
|
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(executor);
|
Assert.NotNull(executor);
|
||||||
|
|
@ -122,17 +124,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateExecutor_ForTaskOfIActionResultReturningMethod_OnPage()
|
public async Task CreateExecutor_ForTaskOfIActionResultReturningMethod()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var actionDescriptor = new CompiledPageActionDescriptor
|
|
||||||
{
|
|
||||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
||||||
};
|
|
||||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.GenericTaskHandler));
|
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.GenericTaskHandler));
|
||||||
|
var handler = new HandlerMethodDescriptor()
|
||||||
|
{
|
||||||
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = CreateParameters(methodInfo),
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
|
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(executor);
|
Assert.NotNull(executor);
|
||||||
|
|
@ -142,17 +145,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateExecutor_ForTaskOfConcreteActionResultReturningMethod_OnPage()
|
public async Task CreateExecutor_ForTaskOfConcreteActionResultReturningMethod()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var actionDescriptor = new CompiledPageActionDescriptor
|
|
||||||
{
|
|
||||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
||||||
};
|
|
||||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.TaskReturningConcreteSubtype));
|
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.TaskReturningConcreteSubtype));
|
||||||
|
var handler = new HandlerMethodDescriptor()
|
||||||
|
{
|
||||||
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = CreateParameters(methodInfo),
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
|
var executor = ExecutorFactory.CreateExecutor(handler);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(executor);
|
Assert.NotNull(executor);
|
||||||
|
|
@ -162,159 +166,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||||
Assert.Equal("value", contentResult.Content);
|
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]
|
[Theory]
|
||||||
[InlineData(nameof(TestPageModel.StringResult))]
|
[InlineData(nameof(TestPageModel.StringResult))]
|
||||||
[InlineData(nameof(TestPageModel.TaskOfObject))]
|
[InlineData(nameof(TestPageModel.TaskOfObject))]
|
||||||
|
|
@ -322,16 +173,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||||
public void CreateExecutor_ThrowsIfTypeIsNotAValidReturnType(string methodName)
|
public void CreateExecutor_ThrowsIfTypeIsNotAValidReturnType(string methodName)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var actionDescriptor = new CompiledPageActionDescriptor
|
|
||||||
{
|
|
||||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
||||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
|
||||||
};
|
|
||||||
var methodInfo = typeof(TestPageModel).GetMethod(methodName);
|
var methodInfo = typeof(TestPageModel).GetMethod(methodName);
|
||||||
|
var handler = new HandlerMethodDescriptor()
|
||||||
|
{
|
||||||
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = CreateParameters(methodInfo),
|
||||||
|
};
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
var ex = Assert.Throws<InvalidOperationException>(() => ExecutorFactory.CreateExecutor(handler));
|
||||||
ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]));
|
|
||||||
Assert.Equal($"Unsupported handler method return type '{methodInfo.ReturnType}'.", ex.Message);
|
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()),
|
BindingInfo = BindingInfo.GetBindingInfo(p.GetCustomAttributes()),
|
||||||
Name = p.Name,
|
Name = p.Name,
|
||||||
Parameter = p,
|
ParameterInfo = p,
|
||||||
ParameterType = p.ParameterType,
|
ParameterType = p.ParameterType,
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,27 +36,34 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
RelativePath = "Path1",
|
RelativePath = "Path1",
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
Func<PageContext, object> factory = _ => null;
|
Func<PageContext, object> factory = _ => null;
|
||||||
Action<PageContext, object> releaser = (_, __) => { };
|
Action<PageContext, object> releaser = (_, __) => { };
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader
|
||||||
|
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
.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>();
|
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||||
pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
pageFactoryProvider
|
||||||
|
.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
.Returns(factory);
|
.Returns(factory);
|
||||||
pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
pageFactoryProvider
|
||||||
|
.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
.Returns(releaser);
|
.Returns(releaser);
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
CreateActionDescriptorCollection(descriptor),
|
||||||
pageFactoryProvider.Object);
|
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
|
// Act
|
||||||
invokerProvider.OnProvidersExecuting(context);
|
invokerProvider.OnProvidersExecuting(context);
|
||||||
|
|
@ -65,106 +72,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.NotNull(context.Result);
|
Assert.NotNull(context.Result);
|
||||||
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||||
var entry = actionInvoker.CacheEntry;
|
var entry = actionInvoker.CacheEntry;
|
||||||
var compiledPageActionDescriptor = Assert.IsType<CompiledPageActionDescriptor>(entry.ActionDescriptor);
|
Assert.Equal(descriptor.RelativePath, entry.ActionDescriptor.RelativePath);
|
||||||
Assert.Equal(descriptor.RelativePath, compiledPageActionDescriptor.RelativePath);
|
|
||||||
Assert.Same(factory, entry.PageFactory);
|
Assert.Same(factory, entry.PageFactory);
|
||||||
Assert.Same(releaser, entry.ReleasePage);
|
Assert.Same(releaser, entry.ReleasePage);
|
||||||
Assert.Null(entry.ModelFactory);
|
Assert.Null(entry.ModelFactory);
|
||||||
Assert.Null(entry.ReleaseModel);
|
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]
|
[Fact]
|
||||||
public void OnProvidersExecuting_WithModel_PopulatesCacheEntry()
|
public void OnProvidersExecuting_WithModel_PopulatesCacheEntry()
|
||||||
{
|
{
|
||||||
|
|
@ -174,36 +88,45 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
RelativePath = "Path1",
|
RelativePath = "Path1",
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
Func<PageContext, object> factory = _ => null;
|
Func<PageContext, object> factory = _ => null;
|
||||||
Action<PageContext, object> releaser = (_, __) => { };
|
Action<PageContext, object> releaser = (_, __) => { };
|
||||||
Func<PageContext, object> modelFactory = _ => null;
|
Func<PageContext, object> modelFactory = _ => null;
|
||||||
Action<PageContext, object> modelDisposer = (_, __) => { };
|
Action<PageContext, object> modelDisposer = (_, __) => { };
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
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)));
|
.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>();
|
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||||
pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
pageFactoryProvider
|
||||||
|
.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
.Returns(factory);
|
.Returns(factory);
|
||||||
pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
pageFactoryProvider
|
||||||
|
.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
.Returns(releaser);
|
.Returns(releaser);
|
||||||
|
|
||||||
var modelFactoryProvider = new Mock<IPageModelFactoryProvider>();
|
var modelFactoryProvider = new Mock<IPageModelFactoryProvider>();
|
||||||
modelFactoryProvider.Setup(f => f.CreateModelFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
modelFactoryProvider
|
||||||
|
.Setup(f => f.CreateModelFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
.Returns(modelFactory);
|
.Returns(modelFactory);
|
||||||
modelFactoryProvider.Setup(f => f.CreateModelDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
modelFactoryProvider
|
||||||
|
.Setup(f => f.CreateModelDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
.Returns(modelDisposer);
|
.Returns(modelDisposer);
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
CreateActionDescriptorCollection(descriptor),
|
||||||
pageFactoryProvider.Object,
|
pageFactoryProvider.Object,
|
||||||
modelFactoryProvider.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
|
// Act
|
||||||
invokerProvider.OnProvidersExecuting(context);
|
invokerProvider.OnProvidersExecuting(context);
|
||||||
|
|
@ -232,18 +155,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
};
|
};
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
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)));
|
.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 razorPageFactoryProvider = new Mock<IRazorPageFactoryProvider>();
|
||||||
|
|
||||||
Func<IRazorPage> factory1 = () => null;
|
Func<IRazorPage> factory1 = () => null;
|
||||||
Func<IRazorPage> factory2 = () => null;
|
Func<IRazorPage> factory2 = () => null;
|
||||||
|
|
||||||
razorPageFactoryProvider
|
razorPageFactoryProvider
|
||||||
.Setup(f => f.CreateFactory("/Home/Path1/_ViewStart.cshtml"))
|
.Setup(f => f.CreateFactory("/Home/Path1/_ViewStart.cshtml"))
|
||||||
.Returns(new RazorPageFactoryResult(factory1, new IChangeToken[0]));
|
.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>() }));
|
.Returns(new RazorPageFactoryResult(factory2, new[] { Mock.Of<IChangeToken>() }));
|
||||||
|
|
||||||
var fileProvider = new TestFileProvider();
|
var fileProvider = new TestFileProvider();
|
||||||
|
|
@ -254,11 +179,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
CreateActionDescriptorCollection(descriptor),
|
||||||
razorPageFactoryProvider: razorPageFactoryProvider.Object,
|
razorPageFactoryProvider: razorPageFactoryProvider.Object,
|
||||||
razorProject: defaultRazorProject);
|
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
|
// Act
|
||||||
invokerProvider.OnProvidersExecuting(context);
|
invokerProvider.OnProvidersExecuting(context);
|
||||||
|
|
@ -270,49 +200,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.Equal(new[] { factory2, factory1 }, entry.ViewStartFactories);
|
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]
|
[Fact]
|
||||||
public void OnProvidersExecuting_CachesEntries()
|
public void OnProvidersExecuting_CachesEntries()
|
||||||
|
|
@ -323,18 +210,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
RelativePath = "Path1",
|
RelativePath = "Path1",
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader
|
||||||
|
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
.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(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object);
|
CreateActionDescriptorCollection(descriptor));
|
||||||
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
|
// Act - 1
|
||||||
invokerProvider.OnProvidersExecuting(context);
|
invokerProvider.OnProvidersExecuting(context);
|
||||||
|
|
@ -363,21 +254,31 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
RelativePath = "Path1",
|
RelativePath = "Path1",
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
var descriptorCollection1 = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
var descriptorCollection1 = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
var descriptorCollection2 = new ActionDescriptorCollection(new[] { descriptor }, version: 2);
|
var descriptorCollection2 = new ActionDescriptorCollection(new[] { descriptor }, version: 2);
|
||||||
|
|
||||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
actionDescriptorProvider.SetupSequence(p => p.ActionDescriptors)
|
actionDescriptorProvider
|
||||||
|
.SetupSequence(p => p.ActionDescriptors)
|
||||||
.Returns(descriptorCollection1)
|
.Returns(descriptorCollection1)
|
||||||
.Returns(descriptorCollection2);
|
.Returns(descriptorCollection2);
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader
|
||||||
|
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.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
|
// Act - 1
|
||||||
invokerProvider.OnProvidersExecuting(context);
|
invokerProvider.OnProvidersExecuting(context);
|
||||||
|
|
@ -397,217 +298,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.NotSame(entry1, entry2);
|
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]
|
[Fact]
|
||||||
public void GetViewStartFactories_FindsFullHeirarchy()
|
public void GetViewStartFactories_FindsFullHeirarchy()
|
||||||
{
|
{
|
||||||
|
|
@ -618,12 +308,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
};
|
};
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
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)));
|
.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();
|
var fileProvider = new TestFileProvider();
|
||||||
fileProvider.AddFile("/View/Deeper/Not_ViewStart.cshtml", "page content");
|
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 razorProject = new TestRazorProject(fileProvider);
|
||||||
|
|
||||||
var mock = new Mock<IRazorPageFactoryProvider>();
|
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>()))
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
mock.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
mock
|
||||||
|
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
mock.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
mock
|
||||||
|
.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
||||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
|
||||||
var razorPageFactoryProvider = mock.Object;
|
var razorPageFactoryProvider = mock.Object;
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
CreateActionDescriptorCollection(descriptor),
|
||||||
pageProvider: null,
|
pageProvider: null,
|
||||||
modelProvider: null,
|
modelProvider: null,
|
||||||
razorPageFactoryProvider: razorPageFactoryProvider,
|
razorPageFactoryProvider: razorPageFactoryProvider,
|
||||||
|
|
@ -675,16 +368,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
ViewEnginePath = "/Pages/Level1/Level2/Index.cshtml"
|
ViewEnginePath = "/Pages/Level1/Level2/Index.cshtml"
|
||||||
};
|
};
|
||||||
|
|
||||||
var compiledPageDescriptor = new CompiledPageActionDescriptor(descriptor)
|
var compiledPageDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||||
{
|
{
|
||||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader
|
||||||
|
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(compiledPageDescriptor);
|
.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();
|
var fileProvider = new TestFileProvider();
|
||||||
fileProvider.AddFile("/_ViewStart.cshtml", "page content");
|
fileProvider.AddFile("/_ViewStart.cshtml", "page content");
|
||||||
|
|
@ -696,13 +389,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var razorProject = new TestRazorProject(fileProvider);
|
var razorProject = new TestRazorProject(fileProvider);
|
||||||
|
|
||||||
var mock = new Mock<IRazorPageFactoryProvider>(MockBehavior.Strict);
|
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>()))
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||||
.Verifiable();
|
.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>()))
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var razorPageFactoryProvider = mock.Object;
|
var razorPageFactoryProvider = mock.Object;
|
||||||
|
|
||||||
var options = new RazorPagesOptions
|
var options = new RazorPagesOptions
|
||||||
{
|
{
|
||||||
RootDirectory = rootDirectory,
|
RootDirectory = rootDirectory,
|
||||||
|
|
@ -710,7 +406,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
CreateActionDescriptorCollection(descriptor),
|
||||||
razorPageFactoryProvider: razorPageFactoryProvider,
|
razorPageFactoryProvider: razorPageFactoryProvider,
|
||||||
razorProject: razorProject,
|
razorProject: razorProject,
|
||||||
razorPagesOptions: options);
|
razorPagesOptions: options);
|
||||||
|
|
@ -734,19 +430,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
};
|
};
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
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)));
|
.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>();
|
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]));
|
.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]));
|
.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]));
|
.Returns(new RazorPageFactoryResult(() => null, new IChangeToken[0]));
|
||||||
|
|
||||||
// No files
|
// No files
|
||||||
|
|
@ -755,7 +453,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
CreateActionDescriptorCollection(descriptor),
|
||||||
pageProvider: null,
|
pageProvider: null,
|
||||||
modelProvider: null,
|
modelProvider: null,
|
||||||
razorPageFactoryProvider: pageFactory.Object,
|
razorPageFactoryProvider: pageFactory.Object,
|
||||||
|
|
@ -770,14 +468,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.Equal(2, factories.Count);
|
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(
|
private static CompiledPageActionDescriptor CreateCompiledPageActionDescriptor(
|
||||||
PageActionDescriptor descriptor,
|
PageActionDescriptor descriptor,
|
||||||
Type pageType = null)
|
Type pageType = null)
|
||||||
|
|
@ -805,7 +495,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
RazorPagesOptions razorPagesOptions = null)
|
RazorPagesOptions razorPagesOptions = null)
|
||||||
{
|
{
|
||||||
var tempDataFactory = new Mock<ITempDataDictionaryFactory>();
|
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>()));
|
.Returns((HttpContext context) => new TempDataDictionary(context, Mock.Of<ITempDataProvider>()));
|
||||||
|
|
||||||
if (razorProject == null)
|
if (razorProject == null)
|
||||||
|
|
@ -838,102 +529,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
NullLoggerFactory.Instance);
|
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);
|
||||||
|
|
||||||
}
|
return actionDescriptorProvider.Object;
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PageWithModel
|
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,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
new FilterItem[0]);
|
new FilterItem[0]);
|
||||||
var invoker = CreateInvoker(
|
var invoker = CreateInvoker(
|
||||||
new[] { filter1.Object, filter2.Object, filter3.Object },
|
new[] { filter1.Object, filter2.Object, filter3.Object },
|
||||||
|
|
@ -408,6 +409,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
new FilterItem[0]);
|
new FilterItem[0]);
|
||||||
var invoker = CreateInvoker(
|
var invoker = CreateInvoker(
|
||||||
new IFilterMetadata[] { filter1.Object, filter2.Object, filter3.Object },
|
new IFilterMetadata[] { filter1.Object, filter2.Object, filter3.Object },
|
||||||
|
|
@ -521,6 +523,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
ViewEnginePath = "/Index.cshtml",
|
ViewEnginePath = "/Index.cshtml",
|
||||||
RelativePath = "/Index.cshtml",
|
RelativePath = "/Index.cshtml",
|
||||||
|
HandlerTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||||
|
ModelTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -607,6 +611,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
(c, model) => { (model as IDisposable)?.Dispose(); },
|
(c, model) => { (model as IDisposable)?.Dispose(); },
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
new FilterItem[0]);
|
new FilterItem[0]);
|
||||||
|
|
||||||
var invoker = new PageActionInvoker(
|
var invoker = new PageActionInvoker(
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -149,20 +151,51 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public async Task ModelBinderFactory_BindsPropertiesOnPage()
|
public async Task ModelBinderFactory_BindsPropertiesOnPage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var type = typeof(PageWithProperty).GetTypeInfo();
|
||||||
|
|
||||||
var actionDescriptor = new CompiledPageActionDescriptor
|
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 modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
||||||
var binder = new TestParameterBinder(new Dictionary<string, object>
|
var binder = new TestParameterBinder(new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
{ nameof(PageWithProperty.Id), 10 },
|
{ nameof(PageWithProperty.Id), 10 },
|
||||||
{ nameof(PageWithProperty.RouteDifferentValue), "route-value" }
|
{ nameof(PageWithProperty.RouteDifferentValue), "route-value" }
|
||||||
});
|
});
|
||||||
|
|
||||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||||
|
|
||||||
var page = new PageWithProperty
|
var page = new PageWithProperty
|
||||||
{
|
{
|
||||||
PageContext = new PageContext(),
|
PageContext = new PageContext()
|
||||||
|
{
|
||||||
|
HttpContext = new DefaultHttpContext(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -172,56 +205,60 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.Equal(10, page.Id);
|
Assert.Equal(10, page.Id);
|
||||||
Assert.Equal("route-value", page.RouteDifferentValue);
|
Assert.Equal("route-value", page.RouteDifferentValue);
|
||||||
Assert.Null(page.PropertyWithNoValue);
|
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]
|
[Fact]
|
||||||
public async Task ModelBinderFactory_BindsPropertiesOnPageModel()
|
public async Task ModelBinderFactory_BindsPropertiesOnPageModel()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var type = typeof(PageModelWithProperty).GetTypeInfo();
|
||||||
|
|
||||||
var actionDescriptor = new CompiledPageActionDescriptor
|
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(),
|
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
||||||
ModelTypeInfo = typeof(PageModelWithProperty).GetTypeInfo(),
|
ModelTypeInfo = typeof(PageModelWithProperty).GetTypeInfo(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var binder = new TestParameterBinder(new Dictionary<string, object>
|
var binder = new TestParameterBinder(new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
{ nameof(PageModelWithProperty.Id), 10 },
|
{ nameof(PageModelWithProperty.Id), 10 },
|
||||||
{ nameof(PageModelWithProperty.RouteDifferentValue), "route-value" }
|
{ nameof(PageModelWithProperty.RouteDifferentValue), "route-value" }
|
||||||
});
|
});
|
||||||
|
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||||
|
|
||||||
var page = new PageWithProperty
|
var page = new PageWithProperty
|
||||||
{
|
{
|
||||||
PageContext = new PageContext(),
|
PageContext = new PageContext()
|
||||||
|
{
|
||||||
|
HttpContext = new DefaultHttpContext(),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var model = new PageModelWithProperty();
|
var model = new PageModelWithProperty();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -235,123 +272,44 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.Equal(10, model.Id);
|
Assert.Equal(10, model.Id);
|
||||||
Assert.Equal("route-value", model.RouteDifferentValue);
|
Assert.Equal("route-value", model.RouteDifferentValue);
|
||||||
Assert.Null(model.PropertyWithNoValue);
|
Assert.Null(model.PropertyWithNoValue);
|
||||||
|
}
|
||||||
|
|
||||||
Assert.Collection(binder.Descriptors,
|
[Fact]
|
||||||
descriptor =>
|
public async Task ModelBinderFactory_PreservesExistingValueIfModelBindingFailed()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(PageModelWithDefaultValue).GetTypeInfo();
|
||||||
|
|
||||||
|
var actionDescriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
BoundProperties = new[]
|
||||||
{
|
{
|
||||||
Assert.Equal(nameof(PageModelWithProperty.Id), descriptor.Name);
|
new PageBoundPropertyDescriptor()
|
||||||
Assert.Equal(BindingSource.Query, descriptor.BindingInfo.BindingSource);
|
{
|
||||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
Name = nameof(PageModelWithDefaultValue.PropertyWithDefaultValue),
|
||||||
Assert.Null(descriptor.BindingInfo.PropertyFilterProvider);
|
ParameterType = typeof(string),
|
||||||
Assert.Equal(typeof(int), descriptor.ParameterType);
|
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]
|
HandlerTypeInfo = type,
|
||||||
public async Task ModelBinderFactory_DiscoversBinderType()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var actionDescriptor = new CompiledPageActionDescriptor
|
|
||||||
{
|
|
||||||
PageTypeInfo = typeof(PageWithProperty).GetTypeInfo(),
|
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 binder = new TestParameterBinder(new Dictionary<string, object>());
|
||||||
|
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||||
|
|
||||||
var page = new PageWithProperty
|
var page = new PageWithProperty
|
||||||
{
|
{
|
||||||
PageContext = new PageContext(),
|
PageContext = new PageContext()
|
||||||
};
|
|
||||||
var model = new PageModelWithPropertyFilterAttribute();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await factory(page, model);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Collection(binder.Descriptors,
|
|
||||||
descriptor =>
|
|
||||||
{
|
{
|
||||||
Assert.Equal(nameof(PageModelWithPropertyFilterAttribute.PropertyWithFilter), descriptor.Name);
|
HttpContext = new DefaultHttpContext(),
|
||||||
Assert.Null(descriptor.BindingInfo.BindingSource);
|
}
|
||||||
Assert.Null(descriptor.BindingInfo.BinderType);
|
};
|
||||||
Assert.IsType<TestPropertyFilterProvider>(descriptor.BindingInfo.PropertyFilterProvider);
|
|
||||||
Assert.Equal(typeof(object), descriptor.ParameterType);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[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 model = new PageModelWithDefaultValue();
|
||||||
var defaultValue = model.PropertyWithDefaultValue;
|
var defaultValue = model.PropertyWithDefaultValue;
|
||||||
|
|
||||||
|
|
@ -362,34 +320,125 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.Equal(defaultValue, model.PropertyWithDefaultValue);
|
Assert.Equal(defaultValue, model.PropertyWithDefaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task ModelBinderFactory_OverwritesDefaultValue()
|
[InlineData("Get")]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("gET")]
|
||||||
|
public async Task ModelBinderFactory_IgnoresPropertyWithoutSupportsGet_WhenRequestIsGet(string method)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var type = typeof(PageModelWithSupportsGetProperty).GetTypeInfo();
|
||||||
|
|
||||||
var actionDescriptor = new CompiledPageActionDescriptor
|
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(),
|
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 modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
var factory = PagePropertyBinderFactory.CreateBinder(binder, modelMetadataProvider, actionDescriptor);
|
||||||
|
|
||||||
var page = new PageWithProperty
|
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
|
// Act
|
||||||
await factory(page, model);
|
await factory(page, model);
|
||||||
|
|
||||||
// Assert
|
// 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
|
private class TestParameterBinder : ParameterBinder
|
||||||
|
|
@ -518,5 +567,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
[ModelBinder]
|
[ModelBinder]
|
||||||
public string PropertyWithDefaultValue { get; set; } = "Hello world";
|
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]
|
[FromRoute]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public void OnGet() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue