Porting DefaultPageHandlerMethodSelector and ExecutorFactory

This commit is contained in:
Pranav K 2017-02-07 12:44:27 -08:00
parent 624909763b
commit 2ff80ffb49
18 changed files with 634 additions and 26 deletions

View File

@ -1,9 +1,19 @@

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MvcSandbox
{
public class TestModel
public class TestModel : PageModel
{
public string Name { get; set; }
public string Name { get; private set; } = "World";
public IActionResult OnPost(string name)
{
Name = name;
return View();
}
}
}

View File

@ -3,9 +3,15 @@
<div class="row">
<div class="col-md-3">
<h2>RazorPages Test</h2>
<h2>RazorPages says Hello @Model.Name!</h2>
<ul>
<li>This file should give you a quick view of a Mvc Raor Page in action.</li>
</ul>
</div>
<form method="post">
<label>Say hello to <input type="text" name="name" /></label>
<input type="submit" value="Say" />
</form>
</div>

View File

@ -4,14 +4,14 @@
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
/// <summary>
/// Allows customization of the of the <see cref="PageModel"/>.
/// Allows customization of the of the <see cref="PageApplicationModel"/>.
/// </summary>
public interface IPageModelConvention
{
/// <summary>
/// Called to apply the convention to the <see cref="PageModel"/>.
/// Called to apply the convention to the <see cref="PageApplicationModel"/>.
/// </summary>
/// <param name="model">The <see cref="PageModel"/>.</param>
void Apply(PageModel model);
/// <param name="model">The <see cref="PageApplicationModel"/>.</param>
void Apply(PageApplicationModel model);
}
}

View File

@ -11,14 +11,14 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
/// <summary>
/// Application model component for RazorPages.
/// </summary>
public class PageModel
public class PageApplicationModel
{
/// <summary>
/// Initializes a new instance of <see cref="PageModel"/>.
/// Initializes a new instance of <see cref="PageApplicationModel"/>.
/// </summary>
/// <param name="relativePath">The application relative path of the page.</param>
/// <param name="viewEnginePath">The path relative to the base path for page discovery.</param>
public PageModel(string relativePath, string viewEnginePath)
public PageApplicationModel(string relativePath, string viewEnginePath)
{
if (relativePath == null)
{
@ -39,10 +39,10 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
}
/// <summary>
/// A copy constructor for <see cref="PageModel"/>.
/// A copy constructor for <see cref="PageApplicationModel"/>.
/// </summary>
/// <param name="other">The <see cref="PageModel"/> to copy from.</param>
public PageModel(PageModel other)
/// <param name="other">The <see cref="PageApplicationModel"/> to copy from.</param>
public PageApplicationModel(PageApplicationModel other)
{
if (other == null)
{
@ -74,7 +74,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
public IList<IFilterMetadata> Filters { get; }
/// <summary>
/// Stores arbitrary metadata properties associated with the <see cref="PageModel"/>.
/// Stores arbitrary metadata properties associated with the <see cref="PageApplicationModel"/>.
/// </summary>
public IDictionary<object, object> Properties { get; }

View File

@ -1,7 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
namespace Microsoft.AspNetCore.Mvc.RazorPages
{
@ -36,5 +38,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
/// Gets or sets the <see cref="TypeInfo"/> of the model.
/// </summary>
public TypeInfo ModelTypeInfo { get; set; }
public IList<HandlerMethodDescriptor> HandlerMethods { get; } = new List<HandlerMethodDescriptor>();
}
}

View File

@ -66,6 +66,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddSingleton<IPageLoader, DefaultPageLoader>();
services.TryAddSingleton<IPageHandlerMethodSelector, DefaultPageHandlerMethodSelector>();
services.TryAddSingleton<PageResultExecutor>();
services.TryAddSingleton<PageArgumentBinder, DefaultPageArgumentBinder>();
}
}
}

View File

@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
private void AddActionDescriptors(IList<ActionDescriptor> actions, RazorProjectItem item, string template)
{
var model = new PageModel(item.CombinedPath, item.PathWithoutExtension);
var model = new PageApplicationModel(item.CombinedPath, item.PathWithoutExtension);
var routePrefix = item.BasePath == "/" ? item.PathWithoutExtension : item.BasePath + item.PathWithoutExtension;
model.Selectors.Add(CreateSelectorModel(routePrefix, template));

View File

@ -0,0 +1,20 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public abstract class PageArgumentBinder
{
public async Task<object> BindModelAsync(PageContext context, Type type, object defaultValue, string name)
{
var result = await BindAsync(context, value: null, name: name, type: type);
return result.IsModelSet ? result.Model : defaultValue;
}
protected abstract Task<ModelBindingResult> BindAsync(PageContext context, object value, string name, Type type);
}
}

View File

@ -0,0 +1,82 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
public class DefaultPageArgumentBinder : PageArgumentBinder
{
private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly IModelBinderFactory _modelBinderFactory;
private readonly IObjectModelValidator _validator;
public DefaultPageArgumentBinder(
IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory,
IObjectModelValidator validator)
{
_modelMetadataProvider = modelMetadataProvider;
_modelBinderFactory = modelBinderFactory;
_validator = validator;
}
protected override async Task<ModelBindingResult> BindAsync(PageContext pageContext, object value, string name, Type type)
{
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);
}
var valueProvider = new CompositeValueProvider(valueProviderFactoryContext.ValueProviders);
var metadata = _modelMetadataProvider.GetMetadataForType(type);
var binder = _modelBinderFactory.CreateBinder(new ModelBinderFactoryContext()
{
BindingInfo = null,
Metadata = metadata,
CacheToken = null,
});
var modelBindingContext = DefaultModelBindingContext.CreateBindingContext(
pageContext,
valueProvider,
metadata,
null,
name);
modelBindingContext.Model = value;
if (modelBindingContext.ValueProvider.ContainsPrefix(name))
{
// We have a match for the parameter name, use that as that prefix.
modelBindingContext.ModelName = name;
}
else
{
// No match, fallback to empty string as the prefix.
modelBindingContext.ModelName = string.Empty;
}
await binder.BindModelAsync(modelBindingContext);
var result = modelBindingContext.Result;
if (result.IsModelSet)
{
_validator.Validate(
pageContext,
modelBindingContext.ValidationState,
modelBindingContext.ModelName,
result.Model);
}
return result;
}
}
}

View File

@ -1,13 +1,143 @@
using System;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class DefaultPageHandlerMethodSelector : IPageHandlerMethodSelector
{
public HandlerMethodDescriptor Select(PageContext context)
{
var handlers = new List<HandlerMethodAndMetadata>(context.ActionDescriptor.HandlerMethods.Count);
for (var i = 0; i < context.ActionDescriptor.HandlerMethods.Count; i++)
{
handlers.Add(HandlerMethodAndMetadata.Create(context.ActionDescriptor.HandlerMethods[i]));
}
for (var i = handlers.Count - 1; i >= 0; i--)
{
var handler = handlers[i];
if (handler.HttpMethod != null &&
!string.Equals(handler.HttpMethod, context.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase))
{
handlers.RemoveAt(i);
}
}
var formaction = Convert.ToString(context.RouteData.Values["formaction"]);
for (var i = handlers.Count - 1; i >= 0; i--)
{
var handler = handlers[i];
if (handler.Formaction != null &&
!string.Equals(handler.Formaction, formaction, StringComparison.OrdinalIgnoreCase))
{
handlers.RemoveAt(i);
}
}
var ambiguousMatches = (List<HandlerMethodDescriptor>)null;
var best = (HandlerMethodAndMetadata?)null;
for (var i = 2; i >= 0; i--)
{
for (var j = 0; j < handlers.Count; j++)
{
var handler = handlers[j];
if (handler.GetScore() == i)
{
if (best == null)
{
best = handler;
continue;
}
if (ambiguousMatches == null)
{
ambiguousMatches = new List<HandlerMethodDescriptor>();
ambiguousMatches.Add(best.Value.Handler);
}
ambiguousMatches.Add(handler.Handler);
}
}
if (ambiguousMatches != null)
{
throw new InvalidOperationException($"Selecting a handler is ambiguous! Matches: {string.Join(", ", ambiguousMatches)}");
}
if (best != null)
{
return best.Value.Handler;
}
}
return null;
}
// Bad prototype substring implementation :)
private struct HandlerMethodAndMetadata
{
public static HandlerMethodAndMetadata Create(HandlerMethodDescriptor handler)
{
var name = handler.Method.Name;
string httpMethod;
if (name.StartsWith("OnGet", StringComparison.Ordinal))
{
httpMethod = "GET";
}
else if (name.StartsWith("OnPost", StringComparison.Ordinal))
{
httpMethod = "POST";
}
else
{
httpMethod = null;
}
var formactionStart = httpMethod?.Length + 2 ?? 0;
var formactionLength = name.EndsWith("Async", StringComparison.Ordinal)
? name.Length - formactionStart - "Async".Length
: name.Length - formactionStart;
var formaction = formactionLength == 0 ? null : name.Substring(formactionStart, formactionLength);
return new HandlerMethodAndMetadata(handler, httpMethod, formaction);
}
public HandlerMethodAndMetadata(HandlerMethodDescriptor handler, string httpMethod, string formaction)
{
Handler = handler;
HttpMethod = httpMethod;
Formaction = formaction;
}
public HandlerMethodDescriptor Handler { get; }
public string HttpMethod { get; }
public string Formaction { get; }
public int GetScore()
{
if (Formaction != null)
{
return 2;
}
else if (HttpMethod != null)
{
return 1;
}
else
{
return 0;
}
}
}
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
@ -11,7 +12,227 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
public static Func<Page, object, Task<IActionResult>> Create(MethodInfo method)
{
throw new NotImplementedException();
return new Executor()
{
Method = method,
}.Execute;
}
private class Executor
{
public MethodInfo Method { get; set; }
public async Task<IActionResult> Execute(Page page, object model)
{
var handler = HandlerMethod.Create(Method);
var receiver = Method.DeclaringType.IsAssignableFrom(page.GetType()) ? page : model;
var arguments = new object[handler.Parameters.Length];
for (var i = 0; i < handler.Parameters.Length; i++)
{
var parameter = handler.Parameters[i];
arguments[i] = await page.Binder.BindModelAsync(
page.PageContext,
parameter.Type,
parameter.DefaultValue,
parameter.Name);
}
var result = await handler.Execute(receiver, arguments);
return result;
}
}
private class HandlerParameter
{
public string Name { get; set; }
public Type Type { get; set; }
public object DefaultValue { get; set; }
}
private abstract class HandlerMethod
{
public static HandlerMethod Create(MethodInfo method)
{
var methodParameters = method.GetParameters();
var parameters = new HandlerParameter[methodParameters.Length];
for (var i = 0; i < methodParameters.Length; i++)
{
parameters[i] = new HandlerParameter()
{
DefaultValue = methodParameters[i].HasDefaultValue ? methodParameters[i].DefaultValue : null,
Name = methodParameters[i].Name,
Type = methodParameters[i].ParameterType,
};
}
if (method.ReturnType == typeof(Task))
{
return new NonGenericTaskHandlerMethod(parameters, method);
}
else if (method.ReturnType == typeof(void))
{
return new VoidHandlerMethod(parameters, method);
}
else if (
method.ReturnType.IsConstructedGenericType &&
method.ReturnType.GetTypeInfo().GetGenericTypeDefinition() == typeof(Task<>) &&
typeof(IActionResult).IsAssignableFrom(method.ReturnType.GetTypeInfo().GetGenericArguments()[0]))
{
return new GenericTaskHandlerMethod(parameters, method);
}
else if (typeof(IActionResult).IsAssignableFrom(method.ReturnType))
{
return new ActionResultHandlerMethod(parameters, method);
}
else
{
throw new InvalidOperationException("unsupported handler method return type");
}
}
protected static Expression[] Unpack(Expression arguments, HandlerParameter[] parameters)
{
var unpackExpressions = new Expression[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
unpackExpressions[i] = Expression.Convert(Expression.ArrayIndex(arguments, Expression.Constant(i)), parameters[i].Type);
}
return unpackExpressions;
}
protected HandlerMethod(HandlerParameter[] parameters)
{
Parameters = parameters;
}
public HandlerParameter[] Parameters { get; }
public abstract Task<IActionResult> Execute(object receiver, object[] arguments);
}
private class NonGenericTaskHandlerMethod : HandlerMethod
{
private readonly Func<object, object[], Task> _thunk;
public NonGenericTaskHandlerMethod(HandlerParameter[] parameters, MethodInfo method)
: base(parameters)
{
var receiver = Expression.Parameter(typeof(object), "receiver");
var arguments = Expression.Parameter(typeof(object[]), "arguments");
_thunk = Expression.Lambda<Func<object, object[], Task>>(
Expression.Call(
Expression.Convert(receiver, method.DeclaringType),
method,
Unpack(arguments, parameters)),
receiver,
arguments).Compile();
}
public override async Task<IActionResult> Execute(object receiver, object[] arguments)
{
await _thunk(receiver, arguments);
return null;
}
}
private class GenericTaskHandlerMethod : HandlerMethod
{
private static readonly MethodInfo ConvertMethod = typeof(GenericTaskHandlerMethod).GetMethod(
nameof(Convert),
BindingFlags.NonPublic | BindingFlags.Static);
private readonly Func<object, object[], Task<object>> _thunk;
public GenericTaskHandlerMethod(HandlerParameter[] parameters, MethodInfo method)
: base(parameters)
{
var receiver = Expression.Parameter(typeof(object), "receiver");
var arguments = Expression.Parameter(typeof(object[]), "arguments");
_thunk = Expression.Lambda<Func<object, object[], Task<object>>>(
Expression.Call(
ConvertMethod.MakeGenericMethod(method.ReturnType.GenericTypeArguments),
Expression.Convert(
Expression.Call(
Expression.Convert(receiver, method.DeclaringType),
method,
Unpack(arguments, parameters)),
typeof(object))),
receiver,
arguments).Compile();
}
public override async Task<IActionResult> Execute(object receiver, object[] arguments)
{
var result = await _thunk(receiver, arguments);
return (IActionResult)result;
}
private static async Task<object> Convert<T>(object taskAsObject)
{
var task = (Task<T>)taskAsObject;
return (object)await task;
}
}
private class VoidHandlerMethod : HandlerMethod
{
private readonly Action<object, object[]> _thunk;
public VoidHandlerMethod(HandlerParameter[] parameters, MethodInfo method)
: base(parameters)
{
var receiver = Expression.Parameter(typeof(object), "receiver");
var arguments = Expression.Parameter(typeof(object[]), "arguments");
_thunk = Expression.Lambda<Action<object, object[]>>(
Expression.Call(
Expression.Convert(receiver, method.DeclaringType),
method,
Unpack(arguments, parameters)),
receiver,
arguments).Compile();
}
public override Task<IActionResult> Execute(object receiver, object[] arguments)
{
_thunk(receiver, arguments);
return Task.FromResult<IActionResult>(null);
}
}
private class ActionResultHandlerMethod : HandlerMethod
{
private readonly Func<object, object[], IActionResult> _thunk;
public ActionResultHandlerMethod(HandlerParameter[] parameters, MethodInfo method)
: base(parameters)
{
var receiver = Expression.Parameter(typeof(object), "receiver");
var arguments = Expression.Parameter(typeof(object[]), "arguments");
_thunk = Expression.Lambda<Func<object, object[], IActionResult>>(
Expression.Convert(
Expression.Call(
Expression.Convert(receiver, method.DeclaringType),
method,
Unpack(arguments, parameters)),
typeof(IActionResult)),
receiver,
arguments).Compile();
}
public override Task<IActionResult> Execute(object receiver, object[] arguments)
{
return Task.FromResult(_thunk(receiver, arguments));
}
}
}
}

View File

@ -304,6 +304,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var actionDescriptor = _pageContext.ActionDescriptor;
_page = (Page)CacheEntry.PageFactory(_pageContext);
_pageContext.Page = _page;
_pageContext.ValueProviderFactories = _valueProviderFactories;
IRazorPage[] pageStarts;

View File

@ -48,9 +48,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
IRazorPageFactoryProvider razorPageFactoryProvider,
IActionDescriptorCollectionProvider collectionProvider,
IEnumerable<IFilterProvider> filterProviders,
IEnumerable<IValueProviderFactory> valueProviderFactories,
IModelMetadataProvider modelMetadataProvider,
ITempDataDictionaryFactory tempDataFactory,
IOptions<MvcOptions> mvcOptions,
IOptions<HtmlHelperOptions> htmlHelperOptions,
IPageHandlerMethodSelector selector,
RazorProject razorProject,
@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
_razorPageFactoryProvider = razorPageFactoryProvider;
_collectionProvider = collectionProvider;
_filterProviders = filterProviders.ToArray();
_valueProviderFactories = valueProviderFactories.ToArray();
_valueProviderFactories = mvcOptions.Value.ValueProviderFactories.ToArray();
_modelMetadataProvider = modelMetadataProvider;
_tempDataFactory = tempDataFactory;
_htmlHelperOptions = htmlHelperOptions.Value;
@ -180,6 +180,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
modelFactory = _modelFactoryProvider.CreateModelFactory(compiledActionDescriptor);
modelReleaser = _modelFactoryProvider.CreateModelDisposer(compiledActionDescriptor);
if (modelType != compiledType)
{
// If the model and page type are different discover handler methods on the model as well.
PopulateHandlerMethodDescriptors(modelType, compiledActionDescriptor);
}
}
var pageStartFactories = GetPageStartFactories(compiledActionDescriptor);
@ -210,6 +216,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
return pageStartFactories;
}
private static void PopulateHandlerMethodDescriptors(TypeInfo type, CompiledPageActionDescriptor actionDescriptor)
{
var methods = type.GetMethods();
for (var i = 0; i < methods.Length; i++)
{
var method = methods[i];
if (method.Name.StartsWith("OnGet", StringComparison.Ordinal) ||
method.Name.StartsWith("OnPost", StringComparison.Ordinal))
{
actionDescriptor.HandlerMethods.Add(new HandlerMethodDescriptor()
{
Method = method,
});
}
}
}
private class InnerCache
{
public InnerCache(int version)

View File

@ -9,6 +9,7 @@ using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.DependencyInjection;
@ -21,6 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
public abstract class Page : RazorPageBase, IRazorPage
{
private IUrlHelper _urlHelper;
private PageArgumentBinder _binder;
/// <inheritdoc />
public IHtmlContent BodyContent { get; set; }
@ -61,6 +63,29 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
[RazorInject]
public HtmlEncoder HtmlEncoder { get; set; }
public PageArgumentBinder Binder
{
get
{
if (_binder == null)
{
_binder = PageContext.HttpContext.RequestServices.GetRequiredService<PageArgumentBinder>();
}
return _binder;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_binder = value;
}
}
protected override HtmlEncoder Encoder => HtmlEncoder;
protected override TextWriter Writer => ViewContext.Writer;

View File

@ -5,6 +5,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
@ -19,6 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
{
private CompiledPageActionDescriptor _actionDescriptor;
private Page _page;
private IList<IValueProviderFactory> _valueProviderFactories;
/// <summary>
/// Creates an empty <see cref="PageContext"/>.
@ -80,5 +82,30 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
/// Gets or sets the applicable _PageStart instances.
/// </summary>
public IReadOnlyList<IRazorPage> PageStarts { get; set; }
/// <summary>
/// Gets or sets the list of <see cref="IValueProviderFactory"/> instances for the current request.
/// </summary>
public virtual IList<IValueProviderFactory> ValueProviderFactories
{
get
{
if (_valueProviderFactories == null)
{
_valueProviderFactories = new List<IValueProviderFactory>();
}
return _valueProviderFactories;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_valueProviderFactories = value;
}
}
}
}

View File

@ -0,0 +1,58 @@
// 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;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Mvc.RazorPages
{
public abstract class PageModel
{
private PageArgumentBinder _binder;
public PageArgumentBinder Binder
{
get
{
if (_binder == null)
{
_binder = PageContext.HttpContext.RequestServices.GetRequiredService<PageArgumentBinder>();
}
return _binder;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_binder = value;
}
}
public Page Page => PageContext.Page;
[PageContext]
public PageContext PageContext { get; set; }
public ModelStateDictionary ModelState => PageContext.ModelState;
public ViewDataDictionary ViewData => PageContext?.ViewData;
protected IActionResult Redirect(string url)
{
return new RedirectResult(url);
}
protected IActionResult View()
{
return new PageViewResult(Page);
}
}
}

View File

@ -242,8 +242,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
var options = new MvcOptions();
options.Filters.Add(globalFilter);
var convention = new Mock<IPageModelConvention>();
convention.Setup(c => c.Apply(It.IsAny<PageModel>()))
.Callback((PageModel model) =>
convention.Setup(c => c.Apply(It.IsAny<PageApplicationModel>()))
.Callback((PageApplicationModel model) =>
{
model.Filters.Add(localFilter);
});

View File

@ -278,9 +278,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
razorPageFactoryProvider ?? Mock.Of<IRazorPageFactoryProvider>(),
actionDescriptorProvider,
new IFilterProvider[0],
new IValueProviderFactory[0],
new EmptyModelMetadataProvider(),
tempDataFactory.Object,
new TestOptionsManager<MvcOptions>(),
new TestOptionsManager<HtmlHelperOptions>(),
Mock.Of<IPageHandlerMethodSelector>(),
razorProject,