Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Add result filters to pages
Browse files Browse the repository at this point in the history
  • Loading branch information
rynowak committed May 26, 2017
1 parent 84e007a commit 688e518
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 840 deletions.
272 changes: 9 additions & 263 deletions src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,16 @@
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;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
Expand All @@ -34,7 +31,6 @@ public class PageActionInvoker : ResourceInvoker, IActionInvoker
private Page _page;
private object _model;
private ViewContext _viewContext;
private ExceptionContext _exceptionContext;

public PageActionInvoker(
IPageHandlerMethodSelector handlerMethodSelector,
Expand Down Expand Up @@ -75,8 +71,8 @@ public PageActionInvoker(
/// </remarks>
protected override async Task InvokeInnerFilterAsync()
{
var next = State.ResourceInnerBegin;
var scope = Scope.Resource;
var next = State.PageBegin;
var scope = Scope.Invoker;
var state = (object)null;
var isCompleted = false;

Expand Down Expand Up @@ -106,186 +102,6 @@ private Task Next(ref State next, ref Scope scope, ref object state, ref bool is

switch (next)
{
case State.ResourceInnerBegin:
{
goto case State.ExceptionBegin;
}

case State.ExceptionBegin:
{
_cursor.Reset();
goto case State.ExceptionNext;
}

case State.ExceptionNext:
{
var current = _cursor.GetNextFilter<IExceptionFilter, IAsyncExceptionFilter>();
if (current.FilterAsync != null)
{
state = current.FilterAsync;
goto case State.ExceptionAsyncBegin;
}
else if (current.Filter != null)
{
state = current.Filter;
goto case State.ExceptionSyncBegin;
}
else if (scope == Scope.Exception)
{
// All exception filters are on the stack already - so execute the 'inside'.
goto case State.ExceptionInside;
}
else
{
// There are no exception filters - so jump right to 'inside'.
Debug.Assert(scope == Scope.Resource);
goto case State.PageBegin;
}
}

case State.ExceptionAsyncBegin:
{
var task = InvokeNextExceptionFilterAsync();
if (task.Status != TaskStatus.RanToCompletion)
{
next = State.ExceptionAsyncResume;
return task;
}

goto case State.ExceptionAsyncResume;
}

case State.ExceptionAsyncResume:
{
Debug.Assert(state != null);

var filter = (IAsyncExceptionFilter)state;
var exceptionContext = _exceptionContext;

// When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception,
// we'll call the filter. Otherwise there's nothing to do.
if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled)
{
_diagnosticSource.BeforeOnExceptionAsync(exceptionContext, filter);

var task = filter.OnExceptionAsync(exceptionContext);
if (task.Status != TaskStatus.RanToCompletion)
{
next = State.ExceptionAsyncEnd;
return task;
}

goto case State.ExceptionAsyncEnd;
}

goto case State.ExceptionEnd;
}

case State.ExceptionAsyncEnd:
{
Debug.Assert(state != null);
Debug.Assert(_exceptionContext != null);

var filter = (IAsyncExceptionFilter)state;
var exceptionContext = _exceptionContext;

_diagnosticSource.AfterOnExceptionAsync(exceptionContext, filter);

if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled)
{
_logger.ExceptionFilterShortCircuited(filter);
}

goto case State.ExceptionEnd;
}

case State.ExceptionSyncBegin:
{
var task = InvokeNextExceptionFilterAsync();
if (task.Status != TaskStatus.RanToCompletion)
{
next = State.ExceptionSyncEnd;
return task;
}

goto case State.ExceptionSyncEnd;
}

case State.ExceptionSyncEnd:
{
Debug.Assert(state != null);

var filter = (IExceptionFilter)state;
var exceptionContext = _exceptionContext;

// When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception,
// we'll call the filter. Otherwise there's nothing to do.
if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled)
{
_diagnosticSource.BeforeOnException(exceptionContext, filter);

filter.OnException(exceptionContext);

_diagnosticSource.AfterOnException(exceptionContext, filter);

if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled)
{
_logger.ExceptionFilterShortCircuited(filter);
}
}

goto case State.ExceptionEnd;
}

case State.ExceptionInside:
{
goto case State.PageBegin;
}

case State.ExceptionShortCircuit:
{
Debug.Assert(state != null);
Debug.Assert(_exceptionContext != null);

if (scope == Scope.Resource)
{
Debug.Assert(_exceptionContext.Result != null);
_result = _exceptionContext.Result;
}

var task = InvokeResultAsync(_exceptionContext.Result);
if (task.Status != TaskStatus.RanToCompletion)
{
next = State.ResourceInnerEnd;
return task;
}

goto case State.ResourceInnerEnd;
}

case State.ExceptionEnd:
{
var exceptionContext = _exceptionContext;

if (scope == Scope.Exception)
{
isCompleted = true;
return TaskCache.CompletedTask;
}

if (exceptionContext != null)
{
if (exceptionContext.Result != null && !exceptionContext.ExceptionHandled)
{
goto case State.ExceptionShortCircuit;
}

Rethrow(exceptionContext);
}

goto case State.ResourceInnerEnd;
}

case State.PageBegin:
{
var pageContext = _pageContext;
Expand All @@ -297,20 +113,6 @@ private Task Next(ref State next, ref Scope scope, ref object state, ref bool is
}

case State.PageEnd:
{
if (scope == Scope.Exception)
{
// If we're inside an exception filter, let's allow those filters to 'unwind' before
// the result.
isCompleted = true;
return TaskCache.CompletedTask;
}

Debug.Assert(scope == Scope.Resource);
goto case State.ResourceInnerEnd;
}

case State.ResourceInnerEnd:
{
isCompleted = true;
return TaskCache.CompletedTask;
Expand Down Expand Up @@ -348,6 +150,9 @@ private async Task ExecutePageWithPageModelAsync()
_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);
Expand Down Expand Up @@ -388,8 +193,6 @@ private async Task ExecutePageWithPageModelAsync()
pageResult.Page = _page;
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
}

await _result.ExecuteResultAsync(_pageContext);
}

private async Task ExecutePageWithoutPageModelAsync()
Expand All @@ -405,6 +208,9 @@ private async Task ExecutePageWithoutPageModelAsync()

_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;
Expand Down Expand Up @@ -440,8 +246,6 @@ private async Task ExecutePageWithoutPageModelAsync()
pageResult.Page = _page;
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
}

await _result.ExecuteResultAsync(_pageContext);
}

private async Task<object[]> GetArguments(HandlerMethodDescriptor handler)
Expand Down Expand Up @@ -506,74 +310,16 @@ private async Task<IActionResult> ExecuteHandlerMethod(object instance)
return result;
}

private async Task InvokeNextExceptionFilterAsync()
{
try
{
var next = State.ExceptionNext;
var state = (object)null;
var scope = Scope.Exception;
var isCompleted = false;
while (!isCompleted)
{
await Next(ref next, ref scope, ref state, ref isCompleted);
}
}
catch (Exception exception)
{
_exceptionContext = new ExceptionContext(_actionContext, _filters)
{
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception),
};
}
}

private static void Rethrow(ExceptionContext 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
{
Resource,
Exception,
Invoker,
Page,
}

private enum State
{
ResourceInnerBegin,
ExceptionBegin,
ExceptionNext,
ExceptionAsyncBegin,
ExceptionAsyncResume,
ExceptionAsyncEnd,
ExceptionSyncBegin,
ExceptionSyncEnd,
ExceptionInside,
ExceptionShortCircuit,
ExceptionEnd,
PageBegin,
PageEnd,
ResourceInnerEnd,
}
}
}
Loading

0 comments on commit 688e518

Please sign in to comment.