Add Page filters
This commit is contained in:
parent
b1bc67e92e
commit
b796bc0f39
|
|
@ -209,7 +209,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
filter.OnActionExecuted(actionExecutedContext);
|
filter.OnActionExecuted(actionExecutedContext);
|
||||||
|
|
||||||
_diagnosticSource.BeforeOnActionExecuted(actionExecutedContext, filter);
|
_diagnosticSource.AfterOnActionExecuted(actionExecutedContext, filter);
|
||||||
|
|
||||||
goto case State.ActionEnd;
|
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.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
@ -26,19 +26,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
private readonly ParameterBinder _parameterBinder;
|
private readonly ParameterBinder _parameterBinder;
|
||||||
private readonly ITempDataDictionaryFactory _tempDataFactory;
|
private readonly ITempDataDictionaryFactory _tempDataFactory;
|
||||||
private readonly HtmlHelperOptions _htmlHelperOptions;
|
private readonly HtmlHelperOptions _htmlHelperOptions;
|
||||||
|
private readonly CompiledPageActionDescriptor _actionDescriptor;
|
||||||
|
|
||||||
private CompiledPageActionDescriptor _actionDescriptor;
|
private Dictionary<string, object> _arguments;
|
||||||
|
private HandlerMethodDescriptor _handler;
|
||||||
private Page _page;
|
private Page _page;
|
||||||
private object _model;
|
private object _pageModel;
|
||||||
private ViewContext _viewContext;
|
private ViewContext _viewContext;
|
||||||
|
|
||||||
|
private PageHandlerSelectedContext _handlerSelectedContext;
|
||||||
|
private PageHandlerExecutingContext _handlerExecutingContext;
|
||||||
|
private PageHandlerExecutedContext _handlerExecutedContext;
|
||||||
|
|
||||||
public PageActionInvoker(
|
public PageActionInvoker(
|
||||||
IPageHandlerMethodSelector handlerMethodSelector,
|
IPageHandlerMethodSelector handlerMethodSelector,
|
||||||
DiagnosticSource diagnosticSource,
|
DiagnosticSource diagnosticSource,
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
PageContext pageContext,
|
PageContext pageContext,
|
||||||
IFilterMetadata[] filterMetadata,
|
IFilterMetadata[] filterMetadata,
|
||||||
IList<IValueProviderFactory> valueProviderFactories,
|
|
||||||
PageActionInvokerCacheEntry cacheEntry,
|
PageActionInvokerCacheEntry cacheEntry,
|
||||||
ParameterBinder parameterBinder,
|
ParameterBinder parameterBinder,
|
||||||
ITempDataDictionaryFactory tempDataFactory,
|
ITempDataDictionaryFactory tempDataFactory,
|
||||||
|
|
@ -48,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
logger,
|
logger,
|
||||||
pageContext,
|
pageContext,
|
||||||
filterMetadata,
|
filterMetadata,
|
||||||
valueProviderFactories)
|
pageContext.ValueProviderFactories)
|
||||||
{
|
{
|
||||||
_selector = handlerMethodSelector;
|
_selector = handlerMethodSelector;
|
||||||
_pageContext = pageContext;
|
_pageContext = pageContext;
|
||||||
|
|
@ -63,6 +68,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal PageActionInvokerCacheEntry CacheEntry { get; }
|
internal PageActionInvokerCacheEntry CacheEntry { get; }
|
||||||
|
|
||||||
|
private bool HasPageModel => _actionDescriptor.HandlerTypeInfo != _actionDescriptor.PageTypeInfo;
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal PageContext PageContext => _pageContext;
|
internal PageContext PageContext => _pageContext;
|
||||||
|
|
||||||
|
|
@ -81,12 +88,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
await Next(ref next, ref scope, ref state, ref isCompleted);
|
await Next(ref next, ref scope, ref state, ref isCompleted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ReleaseResources()
|
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)
|
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;
|
if (HasPageModel)
|
||||||
var logger = _logger;
|
|
||||||
|
|
||||||
switch (next)
|
|
||||||
{
|
{
|
||||||
case State.PageBegin:
|
// Since this is a PageModel, we need to activate it, and then run a handler method on the model.
|
||||||
{
|
_pageModel = CacheEntry.ModelFactory(_pageContext);
|
||||||
var pageContext = _pageContext;
|
_pageContext.ViewData.Model = _pageModel;
|
||||||
|
|
||||||
_cursor.Reset();
|
return _pageModel;
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return ExecutePageWithoutPageModelAsync();
|
// Since this is a Page without a PageModel, we need to create the Page before running a handler method.
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
_viewContext = new ViewContext(
|
_viewContext = new ViewContext(
|
||||||
_pageContext,
|
_pageContext,
|
||||||
NullView.Instance,
|
NullView.Instance,
|
||||||
|
|
@ -190,39 +125,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
_page = (Page)CacheEntry.PageFactory(_pageContext, _viewContext);
|
_page = (Page)CacheEntry.PageFactory(_pageContext, _viewContext);
|
||||||
|
|
||||||
pageResult.Page = _page;
|
if (_actionDescriptor.ModelTypeInfo == _actionDescriptor.PageTypeInfo)
|
||||||
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
|
{
|
||||||
|
_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.
|
return _selector.Select(_pageContext);
|
||||||
_viewContext = new ViewContext(
|
}
|
||||||
_pageContext,
|
|
||||||
NullView.Instance,
|
|
||||||
_pageContext.ViewData,
|
|
||||||
_tempDataFactory.GetTempData(_pageContext.HttpContext),
|
|
||||||
TextWriter.Null,
|
|
||||||
_htmlHelperOptions);
|
|
||||||
|
|
||||||
_page = (Page)CacheEntry.PageFactory(_pageContext, _viewContext);
|
private Task BindArgumentsAsync()
|
||||||
|
{
|
||||||
// Flow the Page in places where the result filters would flow the controller.
|
// This is a temporary workaround.
|
||||||
_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.
|
|
||||||
PageSaveTempDataPropertyFilter propertyFilter = null;
|
PageSaveTempDataPropertyFilter propertyFilter = null;
|
||||||
for (var i = 0; i < _filters.Length; i++)
|
for (var i = 0; i < _filters.Length; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -235,27 +154,37 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
if (propertyFilter != null)
|
if (propertyFilter != null)
|
||||||
{
|
{
|
||||||
propertyFilter.Subject = _model;
|
propertyFilter.Subject = _instance;
|
||||||
propertyFilter.ApplyTempDataChanges(_pageContext.HttpContext);
|
propertyFilter.ApplyTempDataChanges(_pageContext.HttpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
_result = await ExecuteHandlerMethod(_model);
|
// Perf: Avoid allocating async state machines where possible. We only need the state
|
||||||
if (_result is PageResult pageResult)
|
// 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.
|
return Task.CompletedTask;
|
||||||
pageResult.Page = _page;
|
|
||||||
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
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(
|
var result = await _parameterBinder.BindModelAsync(
|
||||||
_pageContext,
|
_pageContext,
|
||||||
|
|
@ -265,29 +194,50 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
if (result.IsModelSet)
|
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)
|
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;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IActionResult> ExecuteHandlerMethod(object instance)
|
private async Task InvokeHandlerMethodAsync()
|
||||||
{
|
{
|
||||||
IActionResult result = null;
|
var handler = _handler;
|
||||||
|
if (_handler != null)
|
||||||
var handler = _selector.Select(_pageContext);
|
|
||||||
if (handler != null)
|
|
||||||
{
|
{
|
||||||
var arguments = await GetArguments(handler);
|
var arguments = PrepareArguments(_arguments, handler);
|
||||||
|
|
||||||
Func<object, object[], Task<IActionResult>> executor = null;
|
Func<object, object[], Task<IActionResult>> executor = null;
|
||||||
for (var i = 0; i < _actionDescriptor.HandlerMethods.Count; i++)
|
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
|
private enum Scope
|
||||||
|
|
@ -319,6 +677,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
private enum State
|
private enum State
|
||||||
{
|
{
|
||||||
PageBegin,
|
PageBegin,
|
||||||
|
PageSelectHandlerBegin,
|
||||||
|
PageSelectHandlerNext,
|
||||||
|
PageSelectHandlerAsyncBegin,
|
||||||
|
PageSelectHandlerAsyncEnd,
|
||||||
|
PageSelectHandlerSync,
|
||||||
|
PageSelectHandlerEnd,
|
||||||
|
PageNext,
|
||||||
|
PageAsyncBegin,
|
||||||
|
PageAsyncEnd,
|
||||||
|
PageSyncBegin,
|
||||||
|
PageSyncEnd,
|
||||||
|
PageInside,
|
||||||
PageEnd,
|
PageEnd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var pageContext = new PageContext(actionContext)
|
var pageContext = new PageContext(actionContext)
|
||||||
{
|
{
|
||||||
ActionDescriptor = cacheEntry.ActionDescriptor,
|
ActionDescriptor = cacheEntry.ActionDescriptor,
|
||||||
|
ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories),
|
||||||
ViewData = cacheEntry.ViewDataFactory(_modelMetadataProvider, actionContext.ModelState),
|
ViewData = cacheEntry.ViewDataFactory(_modelMetadataProvider, actionContext.ModelState),
|
||||||
ViewStartFactories = cacheEntry.ViewStartFactories.ToList(),
|
ViewStartFactories = cacheEntry.ViewStartFactories.ToList(),
|
||||||
};
|
};
|
||||||
|
|
@ -159,7 +160,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
_logger,
|
_logger,
|
||||||
pageContext,
|
pageContext,
|
||||||
filters,
|
filters,
|
||||||
new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories),
|
|
||||||
cacheEntry,
|
cacheEntry,
|
||||||
_parameterBinder,
|
_parameterBinder,
|
||||||
_tempDataFactory,
|
_tempDataFactory,
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,10 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
@ -14,27 +13,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
internal static class PageLoggerExtensions
|
internal static class PageLoggerExtensions
|
||||||
{
|
{
|
||||||
private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
|
private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
|
||||||
private static readonly Action<ILogger, string, Exception> _pageExecuting;
|
private static readonly Action<ILogger, string, string[], ModelValidationState, Exception> _handlerMethodExecuting;
|
||||||
private static readonly Action<ILogger, string, double, Exception> _pageExecuted;
|
private static readonly Action<ILogger, string, string, Exception> _handlerMethodExecuted;
|
||||||
private static readonly Action<ILogger, object, Exception> _exceptionFilterShortCircuit;
|
|
||||||
private static readonly Action<ILogger, object, Exception> _pageFilterShortCircuit;
|
private static readonly Action<ILogger, object, Exception> _pageFilterShortCircuit;
|
||||||
|
|
||||||
static PageLoggerExtensions()
|
static PageLoggerExtensions()
|
||||||
{
|
{
|
||||||
_pageExecuting = LoggerMessage.Define<string>(
|
// These numbers start at 101 intentionally to avoid conflict with the IDs used by ResourceInvoker.
|
||||||
LogLevel.Debug,
|
|
||||||
1,
|
|
||||||
"Executing page {ActionName}");
|
|
||||||
|
|
||||||
_pageExecuted = LoggerMessage.Define<string, double>(
|
_handlerMethodExecuting = LoggerMessage.Define<string, string[], ModelValidationState>(
|
||||||
LogLevel.Information,
|
LogLevel.Information,
|
||||||
2,
|
101,
|
||||||
"Executed page {ActionName} in {ElapsedMilliseconds}ms");
|
"Executing handler method {HandlerName} with arguments ({Arguments}) - ModelState is {ValidationState}");
|
||||||
|
|
||||||
_exceptionFilterShortCircuit = LoggerMessage.Define<object>(
|
_handlerMethodExecuted = LoggerMessage.Define<string, string>(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
4,
|
102,
|
||||||
"Request was short circuited at exception filter '{ExceptionFilter}'.");
|
"Executed handler method {HandlerName}, returned result {ActionResult}.");
|
||||||
|
|
||||||
_pageFilterShortCircuit = LoggerMessage.Define<object>(
|
_pageFilterShortCircuit = LoggerMessage.Define<object>(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
|
|
@ -42,36 +37,39 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
"Request was short circuited at page filter '{PageFilter}'.");
|
"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);
|
if (logger.IsEnabled(LogLevel.Information))
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
var currentTimestamp = Stopwatch.GetTimestamp();
|
var handlerName = handler.MethodInfo.Name;
|
||||||
var elapsed = new TimeSpan((long)(TimestampToTicks * (currentTimestamp - startTimestamp)));
|
|
||||||
|
|
||||||
_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(
|
public static void ExecutedHandlerMethod(this ILogger logger, PageContext context, HandlerMethodDescriptor handler, IActionResult result)
|
||||||
this ILogger logger,
|
|
||||||
IFilterMetadata filter)
|
|
||||||
{
|
{
|
||||||
_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(
|
public static void PageFilterShortCircuited(
|
||||||
|
|
@ -80,50 +78,5 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
_pageFilterShortCircuit(logger, filter, null);
|
_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" />
|
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Filters\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
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.
|
/// Gets or sets the path relative to the base path for page discovery.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ViewEnginePath { get; set; }
|
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()
|
internal static string FormatPathMustBeAnAppRelativePath()
|
||||||
=> GetString("PathMustBeAnAppRelativePath");
|
=> 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)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -141,4 +141,7 @@
|
||||||
<data name="PathMustBeAnAppRelativePath" xml:space="preserve">
|
<data name="PathMustBeAnAppRelativePath" xml:space="preserve">
|
||||||
<value>Path must be an application relative path that starts with a forward slash '/'.</value>
|
<value>Path must be an application relative path that starts with a forward slash '/'.</value>
|
||||||
</data>
|
</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>
|
</root>
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -10,9 +10,11 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.RazorPages\Microsoft.AspNetCore.Mvc.RazorPages.csproj" />
|
<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.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.Logging.Testing" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" 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="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
|
||||||
<PackageReference Include="Moq" Version="$(MoqVersion)" />
|
<PackageReference Include="Moq" Version="$(MoqVersion)" />
|
||||||
<PackageReference Include="xunit" Version="$(XunitVersion)" />
|
<PackageReference Include="xunit" Version="$(XunitVersion)" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue