diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionFilter.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionFilter.cs
index 7192eb19d6..93e4c4774a 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionFilter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionFilter.cs
@@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public int Order { get; set; } = int.MinValue;
///
- public async Task OnActionExecutionAsync(
+ public Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
@@ -40,23 +40,29 @@ namespace Microsoft.AspNetCore.Mvc.Internal
nameof(ActionExecutingContext)));
}
- IAsyncActionFilter asyncActionFilter;
- IActionFilter actionFilter;
- if ((asyncActionFilter = controller as IAsyncActionFilter) != null)
+ if (controller is IAsyncActionFilter asyncActionFilter)
{
- await asyncActionFilter.OnActionExecutionAsync(context, next);
+ return asyncActionFilter.OnActionExecutionAsync(context, next);
}
- else if ((actionFilter = controller as IActionFilter) != null)
+ else if (controller is IActionFilter actionFilter)
{
- actionFilter.OnActionExecuting(context);
- if (context.Result == null)
- {
- actionFilter.OnActionExecuted(await next());
- }
+ return ExecuteActionFilter(context, next, actionFilter);
}
else
{
- await next();
+ return next();
+ }
+ }
+
+ private static async Task ExecuteActionFilter(
+ ActionExecutingContext context,
+ ActionExecutionDelegate next,
+ IActionFilter actionFilter)
+ {
+ actionFilter.OnActionExecuting(context);
+ if (context.Result == null)
+ {
+ actionFilter.OnActionExecuted(await next());
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerResultFilter.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerResultFilter.cs
index 8b37ddccc7..3e30debccc 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerResultFilter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerResultFilter.cs
@@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public int Order { get; set; } = int.MinValue;
///
- public async Task OnResultExecutionAsync(
+ public Task OnResultExecutionAsync(
ResultExecutingContext context,
ResultExecutionDelegate next)
{
@@ -40,23 +40,29 @@ namespace Microsoft.AspNetCore.Mvc.Internal
nameof(ResultExecutingContext)));
}
- IAsyncResultFilter asyncResultFilter;
- IResultFilter resultFilter;
- if ((asyncResultFilter = controller as IAsyncResultFilter) != null)
+ if (controller is IAsyncResultFilter asyncResultFilter)
{
- await asyncResultFilter.OnResultExecutionAsync(context, next);
+ return asyncResultFilter.OnResultExecutionAsync(context, next);
}
- else if ((resultFilter = controller as IResultFilter) != null)
+ else if (controller is IResultFilter resultFilter)
{
- resultFilter.OnResultExecuting(context);
- if (!context.Cancel)
- {
- resultFilter.OnResultExecuted(await next());
- }
+ return ExecuteResultFilter(context, next, resultFilter);
}
else
{
- await next();
+ return next();
+ }
+ }
+
+ private static async Task ExecuteResultFilter(
+ ResultExecutingContext context,
+ ResultExecutionDelegate next,
+ IResultFilter resultFilter)
+ {
+ resultFilter.OnResultExecuting(context);
+ if (!context.Cancel)
+ {
+ resultFilter.OnResultExecuted(await next());
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Filters/IPageFilter.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Filters/IPageFilter.cs
index 8c9b45ea58..101317c94f 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Filters/IPageFilter.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Filters/IPageFilter.cs
@@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.Filters
void OnPageHandlerExecuting(PageHandlerExecutingContext context);
///
- /// Called after the handler method executes, before the action result.
+ /// Called after the handler method executes, before the action method is invoked.
///
/// The .
void OnPageHandlerExecuted(PageHandlerExecutedContext context);
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageApplicationModelProvider.cs
index 1dae7a10da..de7e25ddb5 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageApplicationModelProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageApplicationModelProvider.cs
@@ -15,6 +15,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public class DefaultPageApplicationModelProvider : IPageApplicationModelProvider
{
private const string ModelPropertyName = "Model";
+ private readonly PageHandlerPageFilter _pageHandlerPageFilter = new PageHandlerPageFilter();
+ private readonly PageHandlerResultFilter _pageHandlerResultFilter = new PageHandlerResultFilter();
///
public int Order => -1000;
@@ -142,6 +144,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
pageModel.Filters.Add(filter);
}
}
+
+ if (typeof(IAsyncPageFilter).IsAssignableFrom(pageModel.HandlerType) ||
+ typeof(IPageFilter).IsAssignableFrom(pageModel.HandlerType))
+ {
+ pageModel.Filters.Add(_pageHandlerPageFilter);
+ }
+
+ if (typeof(IAsyncResultFilter).IsAssignableFrom(pageModel.HandlerType) ||
+ typeof(IResultFilter).IsAssignableFrom(pageModel.HandlerType))
+ {
+ pageModel.Filters.Add(_pageHandlerResultFilter);
+ }
}
///
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageHandlerPageFilter.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageHandlerPageFilter.cs
new file mode 100644
index 0000000000..662fa6113a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageHandlerPageFilter.cs
@@ -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.Filters;
+
+namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
+{
+ public class PageHandlerPageFilter : IAsyncPageFilter, IOrderedFilter
+ {
+ ///
+ /// Filters on handlers run furthest from the action.
+ /// t
+ public int Order => int.MinValue;
+
+ public Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (next == null)
+ {
+ throw new ArgumentNullException(nameof(next));
+ }
+
+ var handlerInstance = context.HandlerInstance;
+ if (handlerInstance == null)
+ {
+ throw new InvalidOperationException(Resources.FormatPropertyOfTypeCannotBeNull(
+ nameof(context.HandlerInstance),
+ nameof(PageHandlerExecutedContext)));
+ }
+
+ if (handlerInstance is IAsyncPageFilter asyncPageFilter)
+ {
+ return asyncPageFilter.OnPageHandlerExecutionAsync(context, next);
+ }
+ else if (handlerInstance is IPageFilter pageFilter)
+ {
+ return ExecuteSyncFilter(context, next, pageFilter);
+ }
+ else
+ {
+ return next();
+ }
+ }
+
+ public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (context.HandlerInstance is IAsyncPageFilter asyncPageFilter)
+ {
+ return asyncPageFilter.OnPageHandlerSelectionAsync(context);
+ }
+ else if (context.HandlerInstance is IPageFilter pageFilter)
+ {
+ pageFilter.OnPageHandlerSelected(context);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private static async Task ExecuteSyncFilter(
+ PageHandlerExecutingContext context,
+ PageHandlerExecutionDelegate next,
+ IPageFilter pageFilter)
+ {
+ pageFilter.OnPageHandlerExecuting(context);
+ if (context.Result == null)
+ {
+ pageFilter.OnPageHandlerExecuted(await next());
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageHandlerResultFIlter.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageHandlerResultFIlter.cs
new file mode 100644
index 0000000000..9d0b3dcd09
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageHandlerResultFIlter.cs
@@ -0,0 +1,63 @@
+// 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.Filters;
+
+namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
+{
+ public class PageHandlerResultFilter : IAsyncResultFilter, IOrderedFilter
+ {
+ ///
+ /// Filters on handlers run furthest from the action.
+ ///
+ public int Order => int.MinValue;
+
+ public Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (next == null)
+ {
+ throw new ArgumentNullException(nameof(next));
+ }
+
+ var handler = context.Controller;
+ if (handler == null)
+ {
+ throw new InvalidOperationException(Resources.FormatPropertyOfTypeCannotBeNull(
+ nameof(context.Controller),
+ nameof(ResultExecutingContext)));
+ }
+
+ if (handler is IAsyncResultFilter asyncResultFilter)
+ {
+ return asyncResultFilter.OnResultExecutionAsync(context, next);
+ }
+ else if (handler is IResultFilter resultFilter)
+ {
+ return ExecuteSyncFilter(context, next, resultFilter);
+ }
+ else
+ {
+ return next();
+ }
+ }
+
+ private static async Task ExecuteSyncFilter(
+ ResultExecutingContext context,
+ ResultExecutionDelegate next,
+ IResultFilter resultFilter)
+ {
+ resultFilter.OnResultExecuting(context);
+ if (!context.Cancel)
+ {
+ resultFilter.OnResultExecuted(await next());
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs
index 91ea905afe..0114e2a94c 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs
@@ -9,6 +9,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
@@ -21,8 +22,8 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.RazorPages
{
- [PageModelAttribute]
- public abstract class PageModel
+ [PageModel]
+ public abstract class PageModel : IAsyncPageFilter, IPageFilter
{
private IModelMetadataProvider _metadataProvider;
private IModelBinderFactory _modelBinderFactory;
@@ -1601,5 +1602,74 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
model: model);
return ModelState.IsValid;
}
+
+#region IAsyncPageFilter \ IPageFilter
+ ///
+ /// Called after a handler method has been selected, but before model binding occurs.
+ ///
+ /// The .
+ public virtual void OnPageHandlerSelected(PageHandlerSelectedContext context)
+ {
+ }
+
+ ///
+ /// Called before the handler method executes, after model binding is complete.
+ ///
+ /// The .
+ public virtual void OnPageHandlerExecuting(PageHandlerExecutingContext context)
+ {
+ }
+
+ ///
+ /// Called after the handler method executes, before the action method is invoked.
+ ///
+ /// The .
+ public virtual void OnPageHandlerExecuted(PageHandlerExecutedContext context)
+ {
+ }
+
+ ///
+ /// Called asynchronously after the handler method has been selected, but before model binding occurs.
+ ///
+ /// The .
+ /// A that on completion indicates the filter has executed.
+ public virtual Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ OnPageHandlerSelected(context);
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Called asynchronously before the handler method is invoked, after model binding is complete.
+ ///
+ /// The .
+ ///
+ /// The . Invoked to execute the next page filter or the handler method itself.
+ ///
+ /// A that on completion indicates the filter has executed.
+ public virtual async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (next == null)
+ {
+ throw new ArgumentNullException(nameof(next));
+ }
+
+ OnPageHandlerExecuting(context);
+ if (context.Result == null)
+ {
+ OnPageHandlerExecuted(await next());
+ }
+ }
+#endregion
}
-}
\ No newline at end of file
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs
index 4e7dc2f02a..eac69b9e36 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs
@@ -1209,6 +1209,29 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP
Assert.Equal(expected, response.Trim());
}
+ [Fact]
+ public async Task PageHandlerFilterOnPageModelIsExecuted()
+ {
+ // Arrange
+ var expected = "Hello from OnPageHandlerExecuting";
+
+ // Act
+ var response = await Client.GetStringAsync("/ModelAsFilter?message=Hello+world");
+
+ // Assert
+ Assert.Equal(expected, response.Trim());
+ }
+
+ [Fact]
+ public async Task ResultFilterOnPageModelIsExecuted()
+ {
+ // Act
+ var response = await Client.GetAsync("/ModelAsFilter/TestResultFilter");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+ }
+
private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
{
var getResponse = await Client.GetAsync(request.RequestUri);
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/AuthorizationPageApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/AuthorizationPageApplicationModelProviderTest.cs
index feb20c2b98..3d02c51a68 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/AuthorizationPageApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/AuthorizationPageApplicationModelProviderTest.cs
@@ -7,7 +7,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Authorization;
-using Microsoft.AspNetCore.Mvc.Razor;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
@@ -27,7 +26,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
autorizationProvider.OnProvidersExecuting(context);
// Assert
- Assert.Empty(context.PageApplicationModel.Filters);
+ Assert.Collection(
+ context.PageApplicationModel.Filters,
+ f => Assert.IsType(f));
}
private class PageWithAuthorizeHandlers : Page
@@ -59,6 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
// Assert
Assert.Collection(
context.PageApplicationModel.Filters,
+ f => Assert.IsType(f),
f => Assert.IsType(f));
}
@@ -94,7 +96,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
autorizationProvider.OnProvidersExecuting(context);
// Assert
- var authorizeFilter = Assert.IsType(Assert.Single(context.PageApplicationModel.Filters));
+ AuthorizeFilter authorizeFilter = null;
+ Assert.Collection(
+ context.PageApplicationModel.Filters,
+ f => Assert.IsType(f),
+ f => authorizeFilter = Assert.IsType(f));
+
// Basic + Basic2 + Derived authorize
Assert.Equal(3, authorizeFilter.Policy.Requirements.Count);
}
@@ -133,6 +140,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
// Assert
Assert.Collection(
context.PageApplicationModel.Filters,
+ f => Assert.IsType(f),
f => Assert.IsType(f));
}
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageApplicationModelProviderTest.cs
index 3bf8b5f2ed..afc0c41b1a 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageApplicationModelProviderTest.cs
@@ -7,6 +7,7 @@ using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
+using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
@@ -1032,5 +1033,115 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnGetUser() { }
}
+
+ [Fact]
+ public void PopulateFilters_AddsIFilterMetadataAttributesToModel()
+ {
+ // Arrange
+ var provider = new DefaultPageApplicationModelProvider();
+ var typeInfo = typeof(FilterModel).GetTypeInfo();
+ var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
+
+ // Act
+ provider.PopulateFilters(pageModel);
+
+ // Assert
+ Assert.Collection(
+ pageModel.Filters,
+ filter => Assert.IsType(filter));
+ }
+
+ [PageModel]
+ [Serializable]
+ [TypeFilter(typeof(object))]
+ private class FilterModel
+ {
+ }
+
+ [Fact]
+ public void PopulateFilters_AddsPageHandlerPageFilter_IfPageImplementsIAsyncPageFilter()
+ {
+ // Arrange
+ var provider = new DefaultPageApplicationModelProvider();
+ var typeInfo = typeof(ModelImplementingAsyncPageFilter).GetTypeInfo();
+ var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
+
+ // Act
+ provider.PopulateFilters(pageModel);
+
+ // Assert
+ Assert.Collection(
+ pageModel.Filters,
+ filter => Assert.IsType(filter));
+ }
+
+ private class ModelImplementingAsyncPageFilter : IAsyncPageFilter
+ {
+ public Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ [Fact]
+ public void PopulateFilters_AddsPageHandlerPageFilter_IfPageImplementsIPageFilter()
+ {
+ // Arrange
+ var provider = new DefaultPageApplicationModelProvider();
+ var typeInfo = typeof(ModelImplementingPageFilter).GetTypeInfo();
+ var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
+
+ // Act
+ provider.PopulateFilters(pageModel);
+
+ // Assert
+ Assert.Collection(
+ pageModel.Filters,
+ filter => Assert.IsType(filter));
+ }
+
+ private class ModelImplementingPageFilter : IPageFilter
+ {
+ public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnPageHandlerSelected(PageHandlerSelectedContext context)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ [Fact]
+ public void PopulateFilters_AddsPageHandlerPageFilter_ForModelDerivingFromTypeImplementingPageFilter()
+ {
+ // Arrange
+ var provider = new DefaultPageApplicationModelProvider();
+ var typeInfo = typeof(DerivedFromPageModel).GetTypeInfo();
+ var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
+
+ // Act
+ provider.PopulateFilters(pageModel);
+
+ // Assert
+ Assert.Collection(
+ pageModel.Filters,
+ filter => Assert.IsType(filter),
+ filter => Assert.IsType(filter));
+ }
+
+ [ServiceFilter(typeof(IServiceProvider))]
+ private class DerivedFromPageModel : PageModel { }
}
}
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageHandlerPageFilterTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageHandlerPageFilterTest.cs
new file mode 100644
index 0000000000..6b8e1e5d17
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageHandlerPageFilterTest.cs
@@ -0,0 +1,181 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
+using Microsoft.AspNetCore.Routing;
+using Moq;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
+{
+ public class PageHandlerPageFilterTest
+ {
+ [Fact]
+ public async Task OnPageHandlerExecutionAsync_ExecutesAsyncFilters()
+ {
+ // Arrange
+ var pageContext = new PageContext(new ActionContext(
+ new DefaultHttpContext(),
+ new RouteData(),
+ new PageActionDescriptor(),
+ new ModelStateDictionary()));
+ var model = new Mock();
+
+ var pageHandlerExecutingContext = new PageHandlerExecutingContext(
+ pageContext,
+ Array.Empty(),
+ new HandlerMethodDescriptor(),
+ new Dictionary(),
+ model.Object);
+ var pageHandlerExecutedContext = new PageHandlerExecutedContext(
+ pageContext,
+ Array.Empty(),
+ new HandlerMethodDescriptor(),
+ model.Object);
+ PageHandlerExecutionDelegate next = () => Task.FromResult(pageHandlerExecutedContext);
+
+ var modelAsFilter = model.As();
+ modelAsFilter
+ .Setup(f => f.OnPageHandlerExecutionAsync(pageHandlerExecutingContext, next))
+ .Returns(Task.CompletedTask)
+ .Verifiable();
+
+ var pageHandlerPageFilter = new PageHandlerPageFilter();
+
+ // Act
+ await pageHandlerPageFilter.OnPageHandlerExecutionAsync(pageHandlerExecutingContext, next);
+
+ // Assert
+ modelAsFilter.Verify();
+ }
+
+ [Fact]
+ public async Task OnPageHandlerExecutionAsync_ExecutesSyncFilters()
+ {
+ // Arrange
+ var pageContext = new PageContext(new ActionContext(
+ new DefaultHttpContext(),
+ new RouteData(),
+ new PageActionDescriptor(),
+ new ModelStateDictionary()));
+ var model = new Mock