Provide a way within the Page/PageModel to run code before any handler runs
Fixes #6606
This commit is contained in:
parent
236ef5d1d1
commit
197ef139d6
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
public int Order { get; set; } = int.MinValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
public int Order { get; set; } = int.MinValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.Filters
|
|||
void OnPageHandlerExecuting(PageHandlerExecutingContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Called after the handler method executes, before the action result.
|
||||
/// Called after the handler method executes, before the action method is invoked.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageHandlerExecutedContext"/>.</param>
|
||||
void OnPageHandlerExecuted(PageHandlerExecutedContext context);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <remarks>
|
||||
/// Filters on handlers run furthest from the action.
|
||||
/// </remarks>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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <remarks>
|
||||
/// Filters on handlers run furthest from the action.
|
||||
/// </remarks>
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
/// <summary>
|
||||
/// Called after a handler method has been selected, but before model binding occurs.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageHandlerSelectedContext"/>.</param>
|
||||
public virtual void OnPageHandlerSelected(PageHandlerSelectedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before the handler method executes, after model binding is complete.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageHandlerExecutingContext"/>.</param>
|
||||
public virtual void OnPageHandlerExecuting(PageHandlerExecutingContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after the handler method executes, before the action method is invoked.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageHandlerExecutedContext"/>.</param>
|
||||
public virtual void OnPageHandlerExecuted(PageHandlerExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called asynchronously after the handler method has been selected, but before model binding occurs.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageHandlerSelectedContext"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> that on completion indicates the filter has executed.</returns>
|
||||
public virtual Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
OnPageHandlerSelected(context);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called asynchronously before the handler method is invoked, after model binding is complete.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageHandlerExecutingContext"/>.</param>
|
||||
/// <param name="next">
|
||||
/// The <see cref="PageHandlerExecutionDelegate"/>. Invoked to execute the next page filter or the handler method itself.
|
||||
/// </param>
|
||||
/// <returns>A <see cref="Task"/> that on completion indicates the filter has executed.</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<PageHandlerPageFilter>(f));
|
||||
}
|
||||
|
||||
private class PageWithAuthorizeHandlers : Page
|
||||
|
|
@ -59,6 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Assert
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.Filters,
|
||||
f => Assert.IsType<PageHandlerPageFilter>(f),
|
||||
f => Assert.IsType<AuthorizeFilter>(f));
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +96,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
autorizationProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(context.PageApplicationModel.Filters));
|
||||
AuthorizeFilter authorizeFilter = null;
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.Filters,
|
||||
f => Assert.IsType<PageHandlerPageFilter>(f),
|
||||
f => authorizeFilter = Assert.IsType<AuthorizeFilter>(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<PageHandlerPageFilter>(f),
|
||||
f => Assert.IsType<AllowAnonymousFilter>(f));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TypeFilterAttribute>(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<PageHandlerPageFilter>(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<PageHandlerPageFilter>(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<ServiceFilterAttribute>(filter),
|
||||
filter => Assert.IsType<PageHandlerPageFilter>(filter));
|
||||
}
|
||||
|
||||
[ServiceFilter(typeof(IServiceProvider))]
|
||||
private class DerivedFromPageModel : PageModel { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<PageModel>();
|
||||
|
||||
var pageHandlerExecutingContext = new PageHandlerExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
new Dictionary<string, object>(),
|
||||
model.Object);
|
||||
var pageHandlerExecutedContext = new PageHandlerExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
model.Object);
|
||||
PageHandlerExecutionDelegate next = () => Task.FromResult(pageHandlerExecutedContext);
|
||||
|
||||
var modelAsFilter = model.As<IAsyncPageFilter>();
|
||||
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<object>();
|
||||
|
||||
var modelAsFilter = model.As<IPageFilter>();
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnPageHandlerExecuting(It.IsAny<PageHandlerExecutingContext>()))
|
||||
.Verifiable();
|
||||
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnPageHandlerExecuted(It.IsAny<PageHandlerExecutedContext>()))
|
||||
.Verifiable();
|
||||
|
||||
var pageHandlerExecutingContext = new PageHandlerExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
new Dictionary<string, object>(),
|
||||
model.Object);
|
||||
var pageHandlerExecutedContext = new PageHandlerExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
model.Object);
|
||||
PageHandlerExecutionDelegate next = () => Task.FromResult(pageHandlerExecutedContext);
|
||||
|
||||
var pageHandlerPageFilter = new PageHandlerPageFilter();
|
||||
|
||||
// Act
|
||||
await pageHandlerPageFilter.OnPageHandlerExecutionAsync(pageHandlerExecutingContext, next);
|
||||
|
||||
// Assert
|
||||
modelAsFilter.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnPageHandlerExecutionAsync_DoesNotInvokeHandlerExecuted_IfResultIsSet()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var model = new Mock<object>();
|
||||
|
||||
var modelAsFilter = model.As<IPageFilter>();
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnPageHandlerExecuting(It.IsAny<PageHandlerExecutingContext>()))
|
||||
.Callback((PageHandlerExecutingContext context) => context.Result = new PageResult())
|
||||
.Verifiable();
|
||||
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnPageHandlerExecuted(It.IsAny<PageHandlerExecutedContext>()))
|
||||
.Throws(new Exception("Shouldn't be called"));
|
||||
|
||||
var pageHandlerExecutingContext = new PageHandlerExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
new Dictionary<string, object>(),
|
||||
model.Object);
|
||||
var pageHandlerExecutedContext = new PageHandlerExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
model.Object);
|
||||
PageHandlerExecutionDelegate next = () => Task.FromResult(pageHandlerExecutedContext);
|
||||
|
||||
var pageHandlerPageFilter = new PageHandlerPageFilter();
|
||||
|
||||
// Act
|
||||
await pageHandlerPageFilter.OnPageHandlerExecutionAsync(pageHandlerExecutingContext, next);
|
||||
|
||||
// Assert
|
||||
modelAsFilter.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnPageHandlerExecutionAsync_InvokesNextDelegateIfHandlerDoesNotImplementFilter()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var model = new object();
|
||||
|
||||
var pageHandlerExecutingContext = new PageHandlerExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
new Dictionary<string, object>(),
|
||||
model);
|
||||
var pageHandlerExecutedContext = new PageHandlerExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
model);
|
||||
var invoked = false;
|
||||
PageHandlerExecutionDelegate next = () =>
|
||||
{
|
||||
invoked = true;
|
||||
return Task.FromResult(pageHandlerExecutedContext);
|
||||
};
|
||||
|
||||
var pageHandlerPageFilter = new PageHandlerPageFilter();
|
||||
|
||||
// Act
|
||||
await pageHandlerPageFilter.OnPageHandlerExecutionAsync(pageHandlerExecutingContext, next);
|
||||
|
||||
// Assert
|
||||
Assert.True(invoked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
// 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.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class PageHandlerResultFilterTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task OnResultExecutionAsync_ExecutesAsyncFilters()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var model = new Mock<PageModel>();
|
||||
|
||||
|
||||
var modelAsFilter = model.As<IAsyncResultFilter>();
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnResultExecutionAsync(It.IsAny<ResultExecutingContext>(), It.IsAny<ResultExecutionDelegate>()))
|
||||
.Returns(Task.CompletedTask)
|
||||
.Verifiable();
|
||||
|
||||
var resultExecutingContext = new ResultExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new PageResult(),
|
||||
model.Object);
|
||||
var resultExecutedContext = new ResultExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
resultExecutingContext.Result,
|
||||
model.Object);
|
||||
ResultExecutionDelegate next = () => Task.FromResult(resultExecutedContext);
|
||||
|
||||
var pageHandlerResultFilter = new PageHandlerResultFilter();
|
||||
|
||||
// Act
|
||||
await pageHandlerResultFilter.OnResultExecutionAsync(resultExecutingContext, next);
|
||||
|
||||
// Assert
|
||||
modelAsFilter.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnResultExecutionAsyn_ExecutesSyncFilters()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var model = new Mock<object>();
|
||||
|
||||
var modelAsFilter = model.As<IResultFilter>();
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()))
|
||||
.Verifiable();
|
||||
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()))
|
||||
.Verifiable();
|
||||
|
||||
var resultExecutingContext = new ResultExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new PageResult(),
|
||||
model.Object);
|
||||
var resultExecutedContext = new ResultExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
resultExecutingContext.Result,
|
||||
model.Object);
|
||||
ResultExecutionDelegate next = () => Task.FromResult(resultExecutedContext);
|
||||
|
||||
var pageHandlerResultFilter = new PageHandlerResultFilter();
|
||||
|
||||
// Act
|
||||
await pageHandlerResultFilter.OnResultExecutionAsync(resultExecutingContext, next);
|
||||
|
||||
// Assert
|
||||
modelAsFilter.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnPageHandlerExecutionAsync_DoesNotInvokeResultExecuted_IfCancelled()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var model = new Mock<object>();
|
||||
|
||||
var modelAsFilter = model.As<IResultFilter>();
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()))
|
||||
.Callback((ResultExecutingContext context) => context.Cancel = true)
|
||||
.Verifiable();
|
||||
|
||||
modelAsFilter
|
||||
.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()))
|
||||
.Throws(new Exception("Shouldn't be called"));
|
||||
|
||||
var resultExecutingContext = new ResultExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new PageResult(),
|
||||
model.Object);
|
||||
var resultExecutedContext = new ResultExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
resultExecutingContext.Result,
|
||||
model.Object);
|
||||
ResultExecutionDelegate next = () => Task.FromResult(resultExecutedContext);
|
||||
|
||||
var pageHandlerResultFilter = new PageHandlerResultFilter();
|
||||
|
||||
// Act
|
||||
await pageHandlerResultFilter.OnResultExecutionAsync(resultExecutingContext, next);
|
||||
|
||||
// Assert
|
||||
modelAsFilter.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnPageHandlerExecutionAsync_InvokesNextDelegateIfHandlerDoesNotImplementFilter()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var model = new object();
|
||||
|
||||
var resultExecutingContext = new ResultExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new PageResult(),
|
||||
model);
|
||||
var resultExecutedContext = new ResultExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
resultExecutingContext.Result,
|
||||
model);
|
||||
var invoked = false;
|
||||
ResultExecutionDelegate next = () =>
|
||||
{
|
||||
invoked = true;
|
||||
return Task.FromResult(resultExecutedContext);
|
||||
};
|
||||
|
||||
var pageHandlerResultFilter = new PageHandlerResultFilter();
|
||||
|
||||
// Act
|
||||
await pageHandlerResultFilter.OnResultExecutionAsync(resultExecutingContext, next);
|
||||
|
||||
// Assert
|
||||
Assert.True(invoked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(context.PageApplicationModel.Filters);
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.Filters,
|
||||
f => Assert.IsType<PageHandlerPageFilter>(f));
|
||||
}
|
||||
|
||||
private class PageWithoutResponseCache : Page
|
||||
|
|
@ -59,6 +61,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Collection(
|
||||
context.PageApplicationModel.Filters,
|
||||
f => { },
|
||||
f => Assert.IsType<PageHandlerPageFilter>(f),
|
||||
f =>
|
||||
{
|
||||
var filter = Assert.IsType<ResponseCacheFilter>(f);
|
||||
|
|
@ -104,6 +107,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Collection(
|
||||
context.PageApplicationModel.Filters,
|
||||
f => { },
|
||||
f => Assert.IsType<PageHandlerPageFilter>(f),
|
||||
f =>
|
||||
{
|
||||
var filter = Assert.IsType<ResponseCacheFilter>(f);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.IO;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
|
|
@ -1762,6 +1763,99 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
Assert.Null(pageResult.Page); // This is set by the invoker
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AsyncPageHandlerExecutingMethod_InvokeSyncMethods()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var pageHandlerExecutingContext = new PageHandlerExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
new Dictionary<string, object>(),
|
||||
new object());
|
||||
var pageHandlerExecutedContext = new PageHandlerExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
new object());
|
||||
var testPageModel = new Mock<PageModel> { CallBase = true };
|
||||
testPageModel.Setup(p => p.OnPageHandlerExecuting(pageHandlerExecutingContext))
|
||||
.Verifiable();
|
||||
testPageModel.Setup(p => p.OnPageHandlerExecuted(pageHandlerExecutedContext))
|
||||
.Verifiable();
|
||||
|
||||
// Act
|
||||
await testPageModel.Object.OnPageHandlerExecutionAsync(
|
||||
pageHandlerExecutingContext,
|
||||
() => Task.FromResult(pageHandlerExecutedContext));
|
||||
|
||||
testPageModel.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AsyncPageHandlerExecutingMethod__DoesNotInvokeExecutedMethod_IfResultIsSet()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var pageHandlerExecutingContext = new PageHandlerExecutingContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
new Dictionary<string, object>(),
|
||||
new object());
|
||||
var pageHandlerExecutedContext = new PageHandlerExecutedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new HandlerMethodDescriptor(),
|
||||
new object());
|
||||
var testPageModel = new Mock<PageModel>() { CallBase = true };
|
||||
testPageModel.Setup(p => p.OnPageHandlerExecuting(pageHandlerExecutingContext))
|
||||
.Callback((PageHandlerExecutingContext context) => context.Result = new PageResult())
|
||||
.Verifiable();
|
||||
testPageModel.Setup(p => p.OnPageHandlerExecuted(pageHandlerExecutedContext))
|
||||
.Throws(new Exception("Shouldn't be called"));
|
||||
|
||||
// Act
|
||||
await testPageModel.Object.OnPageHandlerExecutionAsync(
|
||||
pageHandlerExecutingContext,
|
||||
() => Task.FromResult(pageHandlerExecutedContext));
|
||||
|
||||
testPageModel.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AsyncPageHandlerSelectingMethod_InvokeSyncMethods()
|
||||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext(new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
new RouteData(),
|
||||
new PageActionDescriptor(),
|
||||
new ModelStateDictionary()));
|
||||
var pageHandlerSelectedContext = new PageHandlerSelectedContext(
|
||||
pageContext,
|
||||
Array.Empty<IFilterMetadata>(),
|
||||
new object());
|
||||
|
||||
var testPageModel = new Mock<PageModel> { CallBase = true };
|
||||
testPageModel.Setup(p => p.OnPageHandlerSelected(pageHandlerSelectedContext))
|
||||
.Verifiable();
|
||||
|
||||
// Act
|
||||
await testPageModel.Object.OnPageHandlerSelectionAsync(pageHandlerSelectedContext);
|
||||
|
||||
testPageModel.Verify();
|
||||
}
|
||||
|
||||
private class ContentPageModel : PageModel
|
||||
{
|
||||
public IActionResult Content_WithNoEncoding()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
public class ModelAsFilter : PageModel, IResultFilter
|
||||
{
|
||||
public string Message { get; set; }
|
||||
|
||||
public void OnGet(string message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public IActionResult OnGetTestResultFilter() => NotFound();
|
||||
|
||||
public override void OnPageHandlerExecuting(PageHandlerExecutingContext context)
|
||||
{
|
||||
context.HandlerArguments["message"] = "Hello from OnPageHandlerExecuting";
|
||||
}
|
||||
|
||||
public void OnResultExecuted(ResultExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
if (context.Result is NotFoundResult)
|
||||
{
|
||||
context.Result = Redirect("/Different-Location");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@page "{handler?}"
|
||||
@model RazorPagesWebSite.ModelAsFilter
|
||||
@Model.Message
|
||||
Loading…
Reference in New Issue