Add Page filters
This commit is contained in:
parent
b1bc67e92e
commit
b796bc0f39
|
|
@ -209,7 +209,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
filter.OnActionExecuted(actionExecutedContext);
|
||||
|
||||
_diagnosticSource.BeforeOnActionExecuted(actionExecutedContext, filter);
|
||||
_diagnosticSource.AfterOnActionExecuted(actionExecutedContext, filter);
|
||||
|
||||
goto case State.ActionEnd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// A filter that asynchronously surrounds execution of the page handler method.
|
||||
/// </summary>
|
||||
public interface IAsyncPageFilter : IFilterMetadata
|
||||
{
|
||||
/// <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>
|
||||
Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context);
|
||||
|
||||
/// <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>
|
||||
Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// 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.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// A filter that surrounds execution of a page handler method.
|
||||
/// </summary>
|
||||
public interface IPageFilter : IFilterMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Called after a handler method has been selected, but before model binding occurs.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageHandlerSelectedContext"/>.</param>
|
||||
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>
|
||||
void OnPageHandlerExecuting(PageHandlerExecutingContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Called after the handler method executes, before the action result.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="PageHandlerExecutedContext"/>.</param>
|
||||
void OnPageHandlerExecuted(PageHandlerExecutedContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// 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.Runtime.ExceptionServices;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// A context for page filters, used specifically in
|
||||
/// <see cref="IPageFilter.OnPageHandlerExecuted(PageHandlerExecutedContext)"/> and
|
||||
/// <see cref="IAsyncPageFilter.OnPageHandlerExecutionAsync(PageHandlerExecutingContext, PageHandlerExecutionDelegate)"/>.
|
||||
/// </summary>
|
||||
public class PageHandlerExecutedContext : FilterContext
|
||||
{
|
||||
private Exception _exception;
|
||||
private ExceptionDispatchInfo _exceptionDispatchInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PageHandlerExecutedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="pageContext">The <see cref="PageContext"/> associated with the current request.</param>
|
||||
/// <param name="filters">The set of filters associated with the page.</param>
|
||||
/// <param name="handlerMethod">The handler method to be invoked, may be null.</param>
|
||||
/// <param name="handlerInstance">The handler instance associated with the page.</param>
|
||||
public PageHandlerExecutedContext(
|
||||
PageContext pageContext,
|
||||
IList<IFilterMetadata> filters,
|
||||
HandlerMethodDescriptor handlerMethod,
|
||||
object handlerInstance)
|
||||
: base(pageContext, filters)
|
||||
{
|
||||
if (handlerInstance == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handlerInstance));
|
||||
}
|
||||
|
||||
HandlerMethod = handlerMethod;
|
||||
HandlerInstance = handlerInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the descriptor associated with the current page.
|
||||
/// </summary>
|
||||
public new virtual CompiledPageActionDescriptor ActionDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return (CompiledPageActionDescriptor)base.ActionDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an indication that an page filter short-circuited the action and the page filter pipeline.
|
||||
/// </summary>
|
||||
public virtual bool Canceled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the handler instance containing the handler method.
|
||||
/// </summary>
|
||||
public virtual object HandlerInstance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the descriptor for the handler method that was invoked.
|
||||
/// </summary>
|
||||
public virtual HandlerMethodDescriptor HandlerMethod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="System.Exception"/> caught while executing the action or action filters, if
|
||||
/// any.
|
||||
/// </summary>
|
||||
public virtual Exception Exception
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_exception == null && _exceptionDispatchInfo != null)
|
||||
{
|
||||
return _exceptionDispatchInfo.SourceException;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _exception;
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_exceptionDispatchInfo = null;
|
||||
_exception = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="System.Runtime.ExceptionServices.ExceptionDispatchInfo"/> for the
|
||||
/// <see cref="Exception"/>, if an <see cref="System.Exception"/> was caught and this information captured.
|
||||
/// </summary>
|
||||
public virtual ExceptionDispatchInfo ExceptionDispatchInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
return _exceptionDispatchInfo;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_exception = null;
|
||||
_exceptionDispatchInfo = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an indication that the <see cref="Exception"/> has been handled.
|
||||
/// </summary>
|
||||
public virtual bool ExceptionHandled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IActionResult"/>.
|
||||
/// </summary>
|
||||
public virtual IActionResult Result { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// 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 Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// A context for page filters, used specifically in
|
||||
/// <see cref="IPageFilter.OnPageHandlerExecuting(PageHandlerExecutingContext)"/> and
|
||||
/// <see cref="IAsyncPageFilter.OnPageHandlerExecutionAsync(PageHandlerExecutingContext, PageHandlerExecutionDelegate)"/>.
|
||||
/// </summary>
|
||||
public class PageHandlerExecutingContext : FilterContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PageHandlerExecutingContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="pageContext">The <see cref="PageContext"/> associated with the current request.</param>
|
||||
/// <param name="filters">The set of filters associated with the page.</param>
|
||||
/// <param name="handlerMethod">The handler method to be invoked, may be null.</param>
|
||||
/// <param name="handlerArguments">The arguments to provide to the handler method.</param>
|
||||
/// <param name="handlerInstance">The handler instance associated with the page.</param>
|
||||
public PageHandlerExecutingContext(
|
||||
PageContext pageContext,
|
||||
IList<IFilterMetadata> filters,
|
||||
HandlerMethodDescriptor handlerMethod,
|
||||
IDictionary<string, object> handlerArguments,
|
||||
object handlerInstance)
|
||||
: base(pageContext, filters)
|
||||
{
|
||||
if (handlerArguments == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handlerArguments));
|
||||
}
|
||||
|
||||
if (handlerInstance == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handlerInstance));
|
||||
}
|
||||
|
||||
HandlerMethod = handlerMethod;
|
||||
HandlerArguments = handlerArguments;
|
||||
HandlerInstance = handlerInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the descriptor associated with the current page.
|
||||
/// </summary>
|
||||
public new virtual CompiledPageActionDescriptor ActionDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return (CompiledPageActionDescriptor)base.ActionDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IActionResult"/> to execute. Setting <see cref="Result"/> to a non-<c>null</c>
|
||||
/// value inside a page filter will short-circuit the page and any remaining page filters.
|
||||
/// </summary>
|
||||
public virtual IActionResult Result { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arguments to pass when invoking the handler method. Keys are parameter names.
|
||||
/// </summary>
|
||||
public virtual IDictionary<string, object> HandlerArguments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the descriptor for the handler method about to be invoked.
|
||||
/// </summary>
|
||||
public virtual HandlerMethodDescriptor HandlerMethod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object instance containing the handler method.
|
||||
/// </summary>
|
||||
public virtual object HandlerInstance { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// A delegate that asynchronously returns a <see cref="PageHandlerExecutedContext"/> indicating the page or the next
|
||||
/// page filter has executed.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="Task"/> that on completion returns an <see cref="PageHandlerExecutedContext"/>.
|
||||
/// </returns>
|
||||
public delegate Task<PageHandlerExecutedContext> PageHandlerExecutionDelegate();
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// 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 Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// A context for page filters, used specifically in
|
||||
/// <see cref="IPageFilter.OnPageHandlerSelected(PageHandlerSelectedContext)"/> and
|
||||
/// <see cref="IAsyncPageFilter.OnPageHandlerSelectionAsync(PageHandlerSelectedContext)"/>.
|
||||
/// </summary>
|
||||
public class PageHandlerSelectedContext : FilterContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PageHandlerExecutedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="pageContext">The <see cref="PageContext"/> associated with the current request.</param>
|
||||
/// <param name="filters">The set of filters associated with the page.</param>
|
||||
/// <param name="handlerInstance">The handler instance associated with the page.</param>
|
||||
public PageHandlerSelectedContext(
|
||||
PageContext pageContext,
|
||||
IList<IFilterMetadata> filters,
|
||||
object handlerInstance)
|
||||
: base(pageContext, filters)
|
||||
{
|
||||
if (handlerInstance == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handlerInstance));
|
||||
}
|
||||
|
||||
HandlerInstance = handlerInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the descriptor associated with the current page.
|
||||
/// </summary>
|
||||
public new virtual CompiledPageActionDescriptor ActionDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return (CompiledPageActionDescriptor)base.ActionDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the descriptor for the handler method about to be invoked.
|
||||
/// </summary>
|
||||
public virtual HandlerMethodDescriptor HandlerMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object instance containing the handler method.
|
||||
/// </summary>
|
||||
public virtual object HandlerInstance { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public static class MvcRazorPagesDiagnosticSourceExtensions
|
||||
{
|
||||
public static void BeforeHandlerMethod(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
ActionContext actionContext,
|
||||
HandlerMethodDescriptor handlerMethodDescriptor,
|
||||
IDictionary<string, object> arguments,
|
||||
object instance)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(actionContext != null);
|
||||
Debug.Assert(handlerMethodDescriptor != null);
|
||||
Debug.Assert(arguments != null);
|
||||
Debug.Assert(instance != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.BeforeHandlerMethod"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.BeforeHandlerMethod",
|
||||
new
|
||||
{
|
||||
actionContext = actionContext,
|
||||
arguments = arguments,
|
||||
handlerMethodDescriptor = handlerMethodDescriptor,
|
||||
instance = instance,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void AfterHandlerMethod(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
ActionContext actionContext,
|
||||
HandlerMethodDescriptor handlerMethodDescriptor,
|
||||
IDictionary<string, object> arguments,
|
||||
object instance,
|
||||
IActionResult result)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(actionContext != null);
|
||||
Debug.Assert(handlerMethodDescriptor != null);
|
||||
Debug.Assert(arguments != null);
|
||||
Debug.Assert(instance != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.AfterHandlerMethod"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.AfterHandlerMethod",
|
||||
new
|
||||
{
|
||||
actionContext = actionContext,
|
||||
arguments = arguments,
|
||||
handlerMethodDescriptor = handlerMethodDescriptor,
|
||||
instance = instance,
|
||||
result = result
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void BeforeOnPageHandlerExecution(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerExecutingContext handlerExecutionContext,
|
||||
IAsyncPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerExecutionContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerExecution"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerExecution",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerExecutionContext.ActionDescriptor,
|
||||
handlerExecutionContext = handlerExecutionContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void AfterOnPageHandlerExecution(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerExecutedContext handlerExecutedContext,
|
||||
IAsyncPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerExecutedContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.AfterOnPageHandlerExecution"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.AfterOnPageHandlerExecution",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerExecutedContext.ActionDescriptor,
|
||||
handlerExecutedContext = handlerExecutedContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void BeforeOnPageHandlerExecuting(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerExecutingContext handlerExecutingContext,
|
||||
IPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerExecutingContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerExecuting"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerExecuting",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerExecutingContext.ActionDescriptor,
|
||||
handlerExecutingContext = handlerExecutingContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void AfterOnPageHandlerExecuting(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerExecutingContext handlerExecutingContext,
|
||||
IPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerExecutingContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.AfterOnPageHandlerExecuting"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.AfterOnPageHandlerExecuting",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerExecutingContext.ActionDescriptor,
|
||||
handlerExecutingContext = handlerExecutingContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void BeforeOnPageHandlerExecuted(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerExecutedContext handlerExecutedContext,
|
||||
IPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerExecutedContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerExecuted"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerExecuted",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerExecutedContext.ActionDescriptor,
|
||||
handlerExecutedContext = handlerExecutedContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void AfterOnPageHandlerExecuted(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerExecutedContext handlerExecutedContext,
|
||||
IPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerExecutedContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.AfterOnPageHandlerExecuted"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.AfterOnPageHandlerExecuted",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerExecutedContext.ActionDescriptor,
|
||||
handlerExecutedContext = handlerExecutedContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void BeforeOnPageHandlerSelection(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerSelectedContext handlerSelectedContext,
|
||||
IAsyncPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerSelectedContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerSelection"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerSelection",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerSelectedContext.ActionDescriptor,
|
||||
handlerSelectedContext = handlerSelectedContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void AfterOnPageHandlerSelection(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerSelectedContext handlerSelectedContext,
|
||||
IAsyncPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerSelectedContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.AfterOnPageHandlerSelection"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.AfterOnPageHandlerSelection",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerSelectedContext.ActionDescriptor,
|
||||
handlerSelectedContext = handlerSelectedContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void BeforeOnPageHandlerSelected(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerSelectedContext handlerSelectedContext,
|
||||
IPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerSelectedContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerSelected"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.BeforeOnPageHandlerSelected",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerSelectedContext.ActionDescriptor,
|
||||
handlerSelectedContext = handlerSelectedContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void AfterOnPageHandlerSelected(
|
||||
this DiagnosticSource diagnosticSource,
|
||||
PageHandlerSelectedContext handlerSelectedContext,
|
||||
IPageFilter filter)
|
||||
{
|
||||
Debug.Assert(diagnosticSource != null);
|
||||
Debug.Assert(handlerSelectedContext != null);
|
||||
Debug.Assert(filter != null);
|
||||
|
||||
if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.AfterOnPageHandlerSelected"))
|
||||
{
|
||||
diagnosticSource.Write(
|
||||
"Microsoft.AspNetCore.Mvc.AfterOnPageHandlerSelected",
|
||||
new
|
||||
{
|
||||
actionDescriptor = handlerSelectedContext.ActionDescriptor,
|
||||
handlerSelectedContext = handlerSelectedContext,
|
||||
filter = filter
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
|
@ -26,19 +26,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
private readonly ParameterBinder _parameterBinder;
|
||||
private readonly ITempDataDictionaryFactory _tempDataFactory;
|
||||
private readonly HtmlHelperOptions _htmlHelperOptions;
|
||||
private readonly CompiledPageActionDescriptor _actionDescriptor;
|
||||
|
||||
private CompiledPageActionDescriptor _actionDescriptor;
|
||||
private Dictionary<string, object> _arguments;
|
||||
private HandlerMethodDescriptor _handler;
|
||||
private Page _page;
|
||||
private object _model;
|
||||
private object _pageModel;
|
||||
private ViewContext _viewContext;
|
||||
|
||||
private PageHandlerSelectedContext _handlerSelectedContext;
|
||||
private PageHandlerExecutingContext _handlerExecutingContext;
|
||||
private PageHandlerExecutedContext _handlerExecutedContext;
|
||||
|
||||
public PageActionInvoker(
|
||||
IPageHandlerMethodSelector handlerMethodSelector,
|
||||
DiagnosticSource diagnosticSource,
|
||||
ILogger logger,
|
||||
PageContext pageContext,
|
||||
IFilterMetadata[] filterMetadata,
|
||||
IList<IValueProviderFactory> valueProviderFactories,
|
||||
PageActionInvokerCacheEntry cacheEntry,
|
||||
ParameterBinder parameterBinder,
|
||||
ITempDataDictionaryFactory tempDataFactory,
|
||||
|
|
@ -48,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
logger,
|
||||
pageContext,
|
||||
filterMetadata,
|
||||
valueProviderFactories)
|
||||
pageContext.ValueProviderFactories)
|
||||
{
|
||||
_selector = handlerMethodSelector;
|
||||
_pageContext = pageContext;
|
||||
|
|
@ -63,6 +68,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Internal for testing
|
||||
internal PageActionInvokerCacheEntry CacheEntry { get; }
|
||||
|
||||
private bool HasPageModel => _actionDescriptor.HandlerTypeInfo != _actionDescriptor.PageTypeInfo;
|
||||
|
||||
// Internal for testing
|
||||
internal PageContext PageContext => _pageContext;
|
||||
|
||||
|
|
@ -81,12 +88,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
await Next(ref next, ref scope, ref state, ref isCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void ReleaseResources()
|
||||
{
|
||||
if (_model != null && CacheEntry.ReleaseModel != null)
|
||||
if (_pageModel != null && CacheEntry.ReleaseModel != null)
|
||||
{
|
||||
CacheEntry.ReleaseModel(_pageContext, _model);
|
||||
CacheEntry.ReleaseModel(_pageContext, _pageModel);
|
||||
}
|
||||
|
||||
if (_page != null && CacheEntry.ReleasePage != null)
|
||||
|
|
@ -95,91 +102,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
|
||||
private object CreateInstance()
|
||||
{
|
||||
var diagnosticSource = _diagnosticSource;
|
||||
var logger = _logger;
|
||||
|
||||
switch (next)
|
||||
if (HasPageModel)
|
||||
{
|
||||
case State.PageBegin:
|
||||
{
|
||||
var pageContext = _pageContext;
|
||||
// Since this is a PageModel, we need to activate it, and then run a handler method on the model.
|
||||
_pageModel = CacheEntry.ModelFactory(_pageContext);
|
||||
_pageContext.ViewData.Model = _pageModel;
|
||||
|
||||
_cursor.Reset();
|
||||
|
||||
next = State.PageEnd;
|
||||
return ExecutePageAsync();
|
||||
}
|
||||
|
||||
case State.PageEnd:
|
||||
{
|
||||
isCompleted = true;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private Task ExecutePageAsync()
|
||||
{
|
||||
_pageContext.ValueProviderFactories = _valueProviderFactories;
|
||||
|
||||
// There's a fork in the road here between the case where we have a full-fledged PageModel
|
||||
// vs just a Page. We need to know up front because we want to execute handler methods
|
||||
// on the PageModel without instantiating the Page or ViewContext.
|
||||
var hasPageModel = _actionDescriptor.HandlerTypeInfo != _actionDescriptor.PageTypeInfo;
|
||||
if (hasPageModel)
|
||||
{
|
||||
return ExecutePageWithPageModelAsync();
|
||||
return _pageModel;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ExecutePageWithoutPageModelAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecutePageWithPageModelAsync()
|
||||
{
|
||||
// Since this is a PageModel, we need to activate it, and then run a handler method on the model.
|
||||
//
|
||||
// We also know that the model is the pagemodel at this point.
|
||||
Debug.Assert(_actionDescriptor.ModelTypeInfo == _actionDescriptor.HandlerTypeInfo);
|
||||
_model = CacheEntry.ModelFactory(_pageContext);
|
||||
_pageContext.ViewData.Model = _model;
|
||||
|
||||
// Flow the PageModel in places where the result filters would flow the controller.
|
||||
_instance = _model;
|
||||
|
||||
if (CacheEntry.PropertyBinder != null)
|
||||
{
|
||||
await CacheEntry.PropertyBinder(_pageContext, _model);
|
||||
}
|
||||
|
||||
// This is a workaround for not yet having proper filter for Pages.
|
||||
PageSaveTempDataPropertyFilter propertyFilter = null;
|
||||
for (var i = 0; i < _filters.Length; i++)
|
||||
{
|
||||
propertyFilter = _filters[i] as PageSaveTempDataPropertyFilter;
|
||||
if (propertyFilter != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyFilter != null)
|
||||
{
|
||||
propertyFilter.Subject = _model;
|
||||
propertyFilter.ApplyTempDataChanges(_pageContext.HttpContext);
|
||||
}
|
||||
|
||||
_result = await ExecuteHandlerMethod(_model);
|
||||
if (_result is PageResult pageResult)
|
||||
{
|
||||
// If we get here, we are going to render the page, so we need to create it and then initialize
|
||||
// the context so we can run the result.
|
||||
// Since this is a Page without a PageModel, we need to create the Page before running a handler method.
|
||||
_viewContext = new ViewContext(
|
||||
_pageContext,
|
||||
NullView.Instance,
|
||||
|
|
@ -190,39 +125,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
_page = (Page)CacheEntry.PageFactory(_pageContext, _viewContext);
|
||||
|
||||
pageResult.Page = _page;
|
||||
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
|
||||
if (_actionDescriptor.ModelTypeInfo == _actionDescriptor.PageTypeInfo)
|
||||
{
|
||||
_pageContext.ViewData.Model = _page;
|
||||
}
|
||||
|
||||
return _page;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecutePageWithoutPageModelAsync()
|
||||
private HandlerMethodDescriptor SelectHandler()
|
||||
{
|
||||
// Since this is a Page without a PageModel, we need to create the Page before running a handler method.
|
||||
_viewContext = new ViewContext(
|
||||
_pageContext,
|
||||
NullView.Instance,
|
||||
_pageContext.ViewData,
|
||||
_tempDataFactory.GetTempData(_pageContext.HttpContext),
|
||||
TextWriter.Null,
|
||||
_htmlHelperOptions);
|
||||
return _selector.Select(_pageContext);
|
||||
}
|
||||
|
||||
_page = (Page)CacheEntry.PageFactory(_pageContext, _viewContext);
|
||||
|
||||
// Flow the Page in places where the result filters would flow the controller.
|
||||
_instance = _page;
|
||||
|
||||
if (_actionDescriptor.ModelTypeInfo == _actionDescriptor.PageTypeInfo)
|
||||
{
|
||||
_model = _page;
|
||||
_pageContext.ViewData.Model = _model;
|
||||
}
|
||||
|
||||
if (CacheEntry.PropertyBinder != null)
|
||||
{
|
||||
await CacheEntry.PropertyBinder(_pageContext, _model);
|
||||
}
|
||||
|
||||
// This is a workaround for not yet having proper filter for Pages.
|
||||
private Task BindArgumentsAsync()
|
||||
{
|
||||
// This is a temporary workaround.
|
||||
PageSaveTempDataPropertyFilter propertyFilter = null;
|
||||
for (var i = 0; i < _filters.Length; i++)
|
||||
{
|
||||
|
|
@ -235,27 +154,37 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
if (propertyFilter != null)
|
||||
{
|
||||
propertyFilter.Subject = _model;
|
||||
propertyFilter.Subject = _instance;
|
||||
propertyFilter.ApplyTempDataChanges(_pageContext.HttpContext);
|
||||
}
|
||||
|
||||
_result = await ExecuteHandlerMethod(_model);
|
||||
if (_result is PageResult pageResult)
|
||||
// Perf: Avoid allocating async state machines where possible. We only need the state
|
||||
// machine if you need to bind properties or arguments.
|
||||
if (_actionDescriptor.BoundProperties.Count == 0 && (_handler == null || _handler.Parameters.Count == 0))
|
||||
{
|
||||
// If we get here we're going to render the page so we need to initialize the context.
|
||||
pageResult.Page = _page;
|
||||
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return BindArgumentsCoreAsync();
|
||||
}
|
||||
|
||||
private async Task<object[]> GetArguments(HandlerMethodDescriptor handler)
|
||||
private async Task BindArgumentsCoreAsync()
|
||||
{
|
||||
var arguments = new object[handler.Parameters.Count];
|
||||
if (CacheEntry.PropertyBinder != null)
|
||||
{
|
||||
await CacheEntry.PropertyBinder(_pageContext, _instance);
|
||||
}
|
||||
|
||||
if (_handler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var valueProvider = await CompositeValueProvider.CreateAsync(_pageContext, _pageContext.ValueProviderFactories);
|
||||
|
||||
for (var i = 0; i < handler.Parameters.Count; i++)
|
||||
for (var i = 0; i < _handler.Parameters.Count; i++)
|
||||
{
|
||||
var parameter = handler.Parameters[i];
|
||||
var parameter = _handler.Parameters[i];
|
||||
|
||||
var result = await _parameterBinder.BindModelAsync(
|
||||
_pageContext,
|
||||
|
|
@ -265,29 +194,50 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
if (result.IsModelSet)
|
||||
{
|
||||
arguments[i] = result.Model;
|
||||
_arguments[parameter.Name] = result.Model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static object[] PrepareArguments(
|
||||
IDictionary<string, object> argumentsInDictionary,
|
||||
HandlerMethodDescriptor handler)
|
||||
{
|
||||
if (handler.Parameters.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var arguments = new object[handler.Parameters.Count];
|
||||
for (var i = 0; i < arguments.Length; i++)
|
||||
{
|
||||
var parameter = handler.Parameters[i];
|
||||
|
||||
if (argumentsInDictionary.TryGetValue(parameter.ParameterInfo.Name, out var value))
|
||||
{
|
||||
// Do nothing, already set the value.
|
||||
}
|
||||
else if (parameter.ParameterInfo.HasDefaultValue)
|
||||
{
|
||||
arguments[i] = parameter.ParameterInfo.DefaultValue;
|
||||
value = parameter.ParameterInfo.DefaultValue;
|
||||
}
|
||||
else if (parameter.ParameterType.GetTypeInfo().IsValueType)
|
||||
else if (parameter.ParameterInfo.ParameterType.IsValueType)
|
||||
{
|
||||
arguments[i] = Activator.CreateInstance(parameter.ParameterType);
|
||||
value = Activator.CreateInstance(parameter.ParameterInfo.ParameterType);
|
||||
}
|
||||
|
||||
arguments[i] = value;
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private async Task<IActionResult> ExecuteHandlerMethod(object instance)
|
||||
private async Task InvokeHandlerMethodAsync()
|
||||
{
|
||||
IActionResult result = null;
|
||||
|
||||
var handler = _selector.Select(_pageContext);
|
||||
if (handler != null)
|
||||
var handler = _handler;
|
||||
if (_handler != null)
|
||||
{
|
||||
var arguments = await GetArguments(handler);
|
||||
var arguments = PrepareArguments(_arguments, handler);
|
||||
|
||||
Func<object, object[], Task<IActionResult>> executor = null;
|
||||
for (var i = 0; i < _actionDescriptor.HandlerMethods.Count; i++)
|
||||
|
|
@ -299,15 +249,423 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
}
|
||||
|
||||
result = await executor(instance, arguments);
|
||||
_diagnosticSource.BeforeHandlerMethod(_pageContext, handler, _arguments, _instance);
|
||||
_logger.ExecutingHandlerMethod(_pageContext, handler, arguments);
|
||||
|
||||
try
|
||||
{
|
||||
_result = await executor(_instance, arguments);
|
||||
_logger.ExecutedHandlerMethod(_pageContext, handler, _result);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_diagnosticSource.AfterHandlerMethod(_pageContext, handler, _arguments, _instance, _result);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
// Pages have an implicit 'return Page()' even without a handler method.
|
||||
if (_result == null)
|
||||
{
|
||||
result = new PageResult();
|
||||
_result = new PageResult();
|
||||
}
|
||||
|
||||
return result;
|
||||
// We also have some special initialization we need to do for PageResult.
|
||||
if (_result is PageResult pageResult)
|
||||
{
|
||||
// If we used a PageModel then the Page isn't initialized yet.
|
||||
if (_viewContext == null)
|
||||
{
|
||||
_viewContext = new ViewContext(
|
||||
_pageContext,
|
||||
NullView.Instance,
|
||||
_pageContext.ViewData,
|
||||
_tempDataFactory.GetTempData(_pageContext.HttpContext),
|
||||
TextWriter.Null,
|
||||
_htmlHelperOptions);
|
||||
}
|
||||
|
||||
if (_page == null)
|
||||
{
|
||||
_page = (Page)CacheEntry.PageFactory(_pageContext, _viewContext);
|
||||
}
|
||||
|
||||
pageResult.Page = _page;
|
||||
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
|
||||
}
|
||||
}
|
||||
|
||||
private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
|
||||
{
|
||||
switch (next)
|
||||
{
|
||||
case State.PageBegin:
|
||||
{
|
||||
_instance = CreateInstance();
|
||||
|
||||
goto case State.PageSelectHandlerBegin;
|
||||
}
|
||||
|
||||
case State.PageSelectHandlerBegin:
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
_handler = SelectHandler();
|
||||
|
||||
goto case State.PageSelectHandlerNext;
|
||||
}
|
||||
|
||||
case State.PageSelectHandlerNext:
|
||||
|
||||
var currentSelector = _cursor.GetNextFilter<IPageFilter, IAsyncPageFilter>();
|
||||
if (currentSelector.FilterAsync != null)
|
||||
{
|
||||
if (_handlerSelectedContext == null)
|
||||
{
|
||||
_handlerSelectedContext = new PageHandlerSelectedContext(_pageContext, _filters, _instance)
|
||||
{
|
||||
HandlerMethod = _handler,
|
||||
};
|
||||
}
|
||||
|
||||
state = currentSelector.FilterAsync;
|
||||
goto case State.PageSelectHandlerAsyncBegin;
|
||||
}
|
||||
else if (currentSelector.Filter != null)
|
||||
{
|
||||
if (_handlerSelectedContext == null)
|
||||
{
|
||||
_handlerSelectedContext = new PageHandlerSelectedContext(_pageContext, _filters, _instance)
|
||||
{
|
||||
HandlerMethod = _handler,
|
||||
};
|
||||
}
|
||||
|
||||
state = currentSelector.Filter;
|
||||
goto case State.PageSelectHandlerSync;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto case State.PageSelectHandlerEnd;
|
||||
}
|
||||
|
||||
case State.PageSelectHandlerAsyncBegin:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_handlerSelectedContext != null);
|
||||
|
||||
var filter = (IAsyncPageFilter)state;
|
||||
var handlerSelectedContext = _handlerSelectedContext;
|
||||
|
||||
_diagnosticSource.BeforeOnPageHandlerSelection(handlerSelectedContext, filter);
|
||||
|
||||
var task = filter.OnPageHandlerSelectionAsync(handlerSelectedContext);
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.PageSelectHandlerAsyncEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.PageSelectHandlerAsyncEnd;
|
||||
}
|
||||
|
||||
case State.PageSelectHandlerAsyncEnd:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_handlerSelectedContext != null);
|
||||
|
||||
var filter = (IAsyncPageFilter)state;
|
||||
|
||||
_diagnosticSource.AfterOnPageHandlerSelection(_handlerSelectedContext, filter);
|
||||
|
||||
goto case State.PageSelectHandlerNext;
|
||||
}
|
||||
|
||||
case State.PageSelectHandlerSync:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_handlerSelectedContext != null);
|
||||
|
||||
var filter = (IPageFilter)state;
|
||||
var handlerSelectedContext = _handlerSelectedContext;
|
||||
|
||||
_diagnosticSource.BeforeOnPageHandlerSelected(handlerSelectedContext, filter);
|
||||
|
||||
filter.OnPageHandlerSelected(handlerSelectedContext);
|
||||
|
||||
_diagnosticSource.AfterOnPageHandlerSelected(handlerSelectedContext, filter);
|
||||
|
||||
goto case State.PageSelectHandlerNext;
|
||||
}
|
||||
|
||||
case State.PageSelectHandlerEnd:
|
||||
{
|
||||
if (_handlerSelectedContext != null)
|
||||
{
|
||||
_handler = _handlerSelectedContext.HandlerMethod;
|
||||
}
|
||||
|
||||
_arguments = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
_cursor.Reset();
|
||||
|
||||
var task = BindArgumentsAsync();
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.PageNext;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.PageNext;
|
||||
}
|
||||
|
||||
case State.PageNext:
|
||||
{
|
||||
var current = _cursor.GetNextFilter<IPageFilter, IAsyncPageFilter>();
|
||||
if (current.FilterAsync != null)
|
||||
{
|
||||
if (_handlerExecutingContext == null)
|
||||
{
|
||||
_handlerExecutingContext = new PageHandlerExecutingContext(_pageContext, _filters, _handler, _arguments, _instance);
|
||||
}
|
||||
|
||||
state = current.FilterAsync;
|
||||
goto case State.PageAsyncBegin;
|
||||
}
|
||||
else if (current.Filter != null)
|
||||
{
|
||||
if (_handlerExecutingContext == null)
|
||||
{
|
||||
_handlerExecutingContext = new PageHandlerExecutingContext(_pageContext, _filters,_handler, _arguments, _instance);
|
||||
}
|
||||
|
||||
state = current.Filter;
|
||||
goto case State.PageSyncBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto case State.PageInside;
|
||||
}
|
||||
}
|
||||
|
||||
case State.PageAsyncBegin:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_handlerExecutingContext != null);
|
||||
|
||||
var filter = (IAsyncPageFilter)state;
|
||||
var handlerExecutingContext = _handlerExecutingContext;
|
||||
|
||||
_diagnosticSource.BeforeOnPageHandlerExecution(handlerExecutingContext, filter);
|
||||
|
||||
var task = filter.OnPageHandlerExecutionAsync(handlerExecutingContext, InvokeNextPageFilterAwaitedAsync);
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.PageAsyncEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.PageAsyncEnd;
|
||||
}
|
||||
|
||||
case State.PageAsyncEnd:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_handlerExecutingContext != null);
|
||||
|
||||
var filter = (IAsyncPageFilter)state;
|
||||
|
||||
if (_handlerExecutedContext == null)
|
||||
{
|
||||
// If we get here then the filter didn't call 'next' indicating a short circuit.
|
||||
_logger.PageFilterShortCircuited(filter);
|
||||
|
||||
_handlerExecutedContext = new PageHandlerExecutedContext(
|
||||
_pageContext,
|
||||
_filters,
|
||||
_handler,
|
||||
_instance)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _handlerExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnPageHandlerExecution(_handlerExecutedContext, filter);
|
||||
|
||||
goto case State.PageEnd;
|
||||
}
|
||||
|
||||
case State.PageSyncBegin:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_handlerExecutingContext != null);
|
||||
|
||||
var filter = (IPageFilter)state;
|
||||
var handlerExecutingContext = _handlerExecutingContext;
|
||||
|
||||
_diagnosticSource.BeforeOnPageHandlerExecuting(handlerExecutingContext, filter);
|
||||
|
||||
filter.OnPageHandlerExecuting(handlerExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnPageHandlerExecuting(handlerExecutingContext, filter);
|
||||
|
||||
if (handlerExecutingContext.Result != null)
|
||||
{
|
||||
// Short-circuited by setting a result.
|
||||
_logger.PageFilterShortCircuited(filter);
|
||||
|
||||
_handlerExecutedContext = new PageHandlerExecutedContext(
|
||||
_pageContext,
|
||||
_filters,
|
||||
_handler,
|
||||
_instance)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _handlerExecutingContext.Result,
|
||||
};
|
||||
|
||||
goto case State.PageEnd;
|
||||
}
|
||||
|
||||
var task = InvokeNextPageFilterAsync();
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.PageSyncEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.PageSyncEnd;
|
||||
}
|
||||
|
||||
case State.PageSyncEnd:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_handlerExecutingContext != null);
|
||||
Debug.Assert(_handlerExecutedContext != null);
|
||||
|
||||
var filter = (IPageFilter)state;
|
||||
var handlerExecutedContext = _handlerExecutedContext;
|
||||
|
||||
_diagnosticSource.BeforeOnPageHandlerExecuted(handlerExecutedContext, filter);
|
||||
|
||||
filter.OnPageHandlerExecuted(handlerExecutedContext);
|
||||
|
||||
_diagnosticSource.AfterOnPageHandlerExecuted(handlerExecutedContext, filter);
|
||||
|
||||
goto case State.PageEnd;
|
||||
}
|
||||
|
||||
case State.PageInside:
|
||||
{
|
||||
var task = InvokeHandlerMethodAsync();
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.PageEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.PageEnd;
|
||||
}
|
||||
|
||||
case State.PageEnd:
|
||||
{
|
||||
if (scope == Scope.Page)
|
||||
{
|
||||
if (_handlerExecutedContext == null)
|
||||
{
|
||||
_handlerExecutedContext = new PageHandlerExecutedContext(_pageContext, _filters, _handler, _instance)
|
||||
{
|
||||
Result = _result,
|
||||
};
|
||||
}
|
||||
|
||||
isCompleted = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var handlerExecutedContext = _handlerExecutedContext;
|
||||
Rethrow(handlerExecutedContext);
|
||||
|
||||
if (handlerExecutedContext != null)
|
||||
{
|
||||
_result = handlerExecutedContext.Result;
|
||||
}
|
||||
|
||||
isCompleted = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InvokeNextPageFilterAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var next = State.PageNext;
|
||||
var state = (object)null;
|
||||
var scope = Scope.Page;
|
||||
var isCompleted = false;
|
||||
while (!isCompleted)
|
||||
{
|
||||
await Next(ref next, ref scope, ref state, ref isCompleted);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_handlerExecutedContext = new PageHandlerExecutedContext(_pageContext, _filters, _handler, _instance)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception),
|
||||
};
|
||||
}
|
||||
|
||||
Debug.Assert(_handlerExecutedContext != null);
|
||||
}
|
||||
|
||||
private async Task<PageHandlerExecutedContext> InvokeNextPageFilterAwaitedAsync()
|
||||
{
|
||||
Debug.Assert(_handlerExecutingContext != null);
|
||||
if (_handlerExecutingContext.Result != null)
|
||||
{
|
||||
// If we get here, it means that an async filter set a result AND called next(). This is forbidden.
|
||||
var message = Resources.FormatAsyncPageFilter_InvalidShortCircuit(
|
||||
typeof(IAsyncPageFilter).Name,
|
||||
nameof(PageHandlerExecutingContext.Result),
|
||||
typeof(PageHandlerExecutingContext).Name,
|
||||
typeof(PageHandlerExecutionDelegate).Name);
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
await InvokeNextPageFilterAsync();
|
||||
|
||||
Debug.Assert(_handlerExecutedContext != null);
|
||||
return _handlerExecutedContext;
|
||||
}
|
||||
|
||||
private static void Rethrow(PageHandlerExecutedContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionDispatchInfo != null)
|
||||
{
|
||||
context.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
|
||||
if (context.Exception != null)
|
||||
{
|
||||
throw context.Exception;
|
||||
}
|
||||
}
|
||||
|
||||
private enum Scope
|
||||
|
|
@ -319,6 +677,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
private enum State
|
||||
{
|
||||
PageBegin,
|
||||
PageSelectHandlerBegin,
|
||||
PageSelectHandlerNext,
|
||||
PageSelectHandlerAsyncBegin,
|
||||
PageSelectHandlerAsyncEnd,
|
||||
PageSelectHandlerSync,
|
||||
PageSelectHandlerEnd,
|
||||
PageNext,
|
||||
PageAsyncBegin,
|
||||
PageAsyncEnd,
|
||||
PageSyncBegin,
|
||||
PageSyncEnd,
|
||||
PageInside,
|
||||
PageEnd,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var pageContext = new PageContext(actionContext)
|
||||
{
|
||||
ActionDescriptor = cacheEntry.ActionDescriptor,
|
||||
ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories),
|
||||
ViewData = cacheEntry.ViewDataFactory(_modelMetadataProvider, actionContext.ModelState),
|
||||
ViewStartFactories = cacheEntry.ViewStartFactories.ToList(),
|
||||
};
|
||||
|
|
@ -159,7 +160,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
_logger,
|
||||
pageContext,
|
||||
filters,
|
||||
new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories),
|
||||
cacheEntry,
|
||||
_parameterBinder,
|
||||
_tempDataFactory,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
|
|
@ -14,27 +13,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
internal static class PageLoggerExtensions
|
||||
{
|
||||
private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
|
||||
private static readonly Action<ILogger, string, Exception> _pageExecuting;
|
||||
private static readonly Action<ILogger, string, double, Exception> _pageExecuted;
|
||||
private static readonly Action<ILogger, object, Exception> _exceptionFilterShortCircuit;
|
||||
private static readonly Action<ILogger, string, string[], ModelValidationState, Exception> _handlerMethodExecuting;
|
||||
private static readonly Action<ILogger, string, string, Exception> _handlerMethodExecuted;
|
||||
private static readonly Action<ILogger, object, Exception> _pageFilterShortCircuit;
|
||||
|
||||
static PageLoggerExtensions()
|
||||
{
|
||||
_pageExecuting = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
1,
|
||||
"Executing page {ActionName}");
|
||||
// These numbers start at 101 intentionally to avoid conflict with the IDs used by ResourceInvoker.
|
||||
|
||||
_pageExecuted = LoggerMessage.Define<string, double>(
|
||||
_handlerMethodExecuting = LoggerMessage.Define<string, string[], ModelValidationState>(
|
||||
LogLevel.Information,
|
||||
2,
|
||||
"Executed page {ActionName} in {ElapsedMilliseconds}ms");
|
||||
101,
|
||||
"Executing handler method {HandlerName} with arguments ({Arguments}) - ModelState is {ValidationState}");
|
||||
|
||||
_exceptionFilterShortCircuit = LoggerMessage.Define<object>(
|
||||
_handlerMethodExecuted = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Debug,
|
||||
4,
|
||||
"Request was short circuited at exception filter '{ExceptionFilter}'.");
|
||||
102,
|
||||
"Executed handler method {HandlerName}, returned result {ActionResult}.");
|
||||
|
||||
_pageFilterShortCircuit = LoggerMessage.Define<object>(
|
||||
LogLevel.Debug,
|
||||
|
|
@ -42,36 +37,39 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
"Request was short circuited at page filter '{PageFilter}'.");
|
||||
}
|
||||
|
||||
public static IDisposable PageScope(this ILogger logger, ActionDescriptor actionDescriptor)
|
||||
public static void ExecutingHandlerMethod(this ILogger logger, PageContext context, HandlerMethodDescriptor handler, object[] arguments)
|
||||
{
|
||||
Debug.Assert(logger != null);
|
||||
Debug.Assert(actionDescriptor != null);
|
||||
|
||||
return logger.BeginScope(new PageLogScope(actionDescriptor));
|
||||
}
|
||||
|
||||
public static void ExecutingPage(this ILogger logger, ActionDescriptor action)
|
||||
{
|
||||
_pageExecuting(logger, action.DisplayName, null);
|
||||
}
|
||||
|
||||
public static void ExecutedAction(this ILogger logger, ActionDescriptor action, long startTimestamp)
|
||||
{
|
||||
// Don't log if logging wasn't enabled at start of request as time will be wildly wrong.
|
||||
if (logger.IsEnabled(LogLevel.Information) && startTimestamp != 0)
|
||||
if (logger.IsEnabled(LogLevel.Information))
|
||||
{
|
||||
var currentTimestamp = Stopwatch.GetTimestamp();
|
||||
var elapsed = new TimeSpan((long)(TimestampToTicks * (currentTimestamp - startTimestamp)));
|
||||
var handlerName = handler.MethodInfo.Name;
|
||||
|
||||
_pageExecuted(logger, action.DisplayName, elapsed.TotalMilliseconds, null);
|
||||
string[] convertedArguments;
|
||||
if (arguments == null)
|
||||
{
|
||||
convertedArguments = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
convertedArguments = new string[arguments.Length];
|
||||
for (var i = 0; i < arguments.Length; i++)
|
||||
{
|
||||
convertedArguments[i] = Convert.ToString(arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var validationState = context.ModelState.ValidationState;
|
||||
|
||||
_handlerMethodExecuting(logger, handlerName, convertedArguments, validationState, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExceptionFilterShortCircuited(
|
||||
this ILogger logger,
|
||||
IFilterMetadata filter)
|
||||
public static void ExecutedHandlerMethod(this ILogger logger, PageContext context, HandlerMethodDescriptor handler, IActionResult result)
|
||||
{
|
||||
_exceptionFilterShortCircuit(logger, filter, null);
|
||||
if (logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
var handlerName = handler.MethodInfo.Name;
|
||||
_handlerMethodExecuted(logger, handlerName, Convert.ToString(result), null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PageFilterShortCircuited(
|
||||
|
|
@ -80,50 +78,5 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
_pageFilterShortCircuit(logger, filter, null);
|
||||
}
|
||||
|
||||
private class PageLogScope : IReadOnlyList<KeyValuePair<string, object>>
|
||||
{
|
||||
private readonly ActionDescriptor _action;
|
||||
|
||||
public PageLogScope(ActionDescriptor action)
|
||||
{
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public KeyValuePair<string, object> this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
return new KeyValuePair<string, object>("ActionId", _action.Id);
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
return new KeyValuePair<string, object>("PageName", _action.DisplayName);
|
||||
}
|
||||
throw new IndexOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
|
||||
public int Count => 2;
|
||||
|
||||
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; ++i)
|
||||
{
|
||||
yield return this[i];
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// We don't include the _action.Id here because it's just an opaque guid, and if
|
||||
// you have text logging, you can already use the requestId for correlation.
|
||||
return _action.DisplayName;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,4 +18,8 @@
|
|||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Filters\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
|
||||
|
|
@ -35,5 +36,29 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// Gets or sets the path relative to the base path for page discovery.
|
||||
/// </summary>
|
||||
public string ViewEnginePath { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (base.DisplayName == null && ViewEnginePath != null)
|
||||
{
|
||||
base.DisplayName = ViewEnginePath;
|
||||
}
|
||||
|
||||
return base.DisplayName;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
base.DisplayName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -122,6 +122,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
internal static string FormatPathMustBeAnAppRelativePath()
|
||||
=> GetString("PathMustBeAnAppRelativePath");
|
||||
|
||||
/// <summary>
|
||||
/// If an {0} provides a result value by setting the {1} property of {2} to a non-null value, then it cannot call the next filter by invoking {3}.
|
||||
/// </summary>
|
||||
internal static string AsyncPageFilter_InvalidShortCircuit
|
||||
{
|
||||
get => GetString("AsyncPageFilter_InvalidShortCircuit");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If an {0} provides a result value by setting the {1} property of {2} to a non-null value, then it cannot call the next filter by invoking {3}.
|
||||
/// </summary>
|
||||
internal static string FormatAsyncPageFilter_InvalidShortCircuit(object p0, object p1, object p2, object p3)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("AsyncPageFilter_InvalidShortCircuit"), p0, p1, p2, p3);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -141,4 +141,7 @@
|
|||
<data name="PathMustBeAnAppRelativePath" xml:space="preserve">
|
||||
<value>Path must be an application relative path that starts with a forward slash '/'.</value>
|
||||
</data>
|
||||
<data name="AsyncPageFilter_InvalidShortCircuit" xml:space="preserve">
|
||||
<value>If an {0} provides a result value by setting the {1} property of {2} to a non-null value, then it cannot call the next filter by invoking {3}.</value>
|
||||
</data>
|
||||
</root>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -10,9 +10,11 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.RazorPages\Microsoft.AspNetCore.Mvc.RazorPages.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.TestCommon\Microsoft.AspNetCore.Mvc.TestCommon.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.TestDiagnosticListener\Microsoft.AspNetCore.Mvc.TestDiagnosticListener.csproj" />
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
|
||||
<PackageReference Include="Moq" Version="$(MoqVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XunitVersion)" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue