Filter execution

This commit is contained in:
Yishai Galatzer 2014-03-05 13:11:01 -08:00
parent f669c1c00e
commit 6fed92695b
13 changed files with 170 additions and 15 deletions

View File

@ -25,9 +25,9 @@ namespace MvcSample
public class PassThroughAttribute : AuthorizationFilterAttribute
{
public async override Task Invoke(AuthorizationFilterContext context, Func<AuthorizationFilterContext, Task> next)
public async override Task Invoke(AuthorizationFilterContext context, Func<Task> next)
{
await next(context);
await next();
}
}
}

View File

@ -6,7 +6,7 @@ namespace Microsoft.AspNet.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ActionFilterAttribute : Attribute, IActionFilter, IFilter
{
public abstract Task Invoke(ActionFilterContext context, Func<ActionFilterContext, Task> next);
public abstract Task Invoke(ActionFilterContext context, Func<Task> next);
public int Order { get; set; }
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc
{
@ -14,6 +15,8 @@ namespace Microsoft.AspNet.Mvc
public virtual ActionContext ActionContext { get; private set; }
public virtual IActionResult Result { get; set; }
public virtual Type MethodReturnType { get; private set; }
public virtual object Result { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Filters
{
public class ReflectedActionFilterEndPoint : IActionFilter
{
private readonly Func<object[], Task<object>> _coreMethodInvoker;
private readonly IActionResultFactory _actionResultFactory;
public ReflectedActionFilterEndPoint(Func<object[], Task<object>> coreMethodInvoker,
IActionResultFactory actionResultFactory)
{
_coreMethodInvoker = coreMethodInvoker;
_actionResultFactory = actionResultFactory;
}
public async Task Invoke(ActionFilterContext context, Func<Task> next)
{
// TODO: match the parameter names here.
var tempArray = context.ActionParameters.Values.ToArray(); // seriously broken for now, need to organize names to match.
var actionReturnValue = await _coreMethodInvoker(tempArray);
context.Result = _actionResultFactory.CreateActionResult(context.MethodReturnType,
actionReturnValue,
context.ActionContext);
}
}
}

View File

@ -6,7 +6,7 @@ namespace Microsoft.AspNet.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ActionResultFilterAttribute : Attribute, IActionResultFilter, IFilter
{
public abstract Task Invoke(ActionResultFilterContext context, Func<ActionResultFilterContext, Task> next);
public abstract Task Invoke(ActionResultFilterContext context, Func<Task> next);
public int Order { get; set; }
}

View File

@ -2,9 +2,10 @@
{
public class ActionResultFilterContext
{
public ActionResultFilterContext(ActionContext actionContext)
public ActionResultFilterContext(ActionContext actionContext, IActionResult initialResult)
{
ActionContext = actionContext;
Result = initialResult;
}
public ActionContext ActionContext { get; private set; }

View File

@ -0,0 +1,13 @@
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Filters
{
public class ActionResultFilterEndPoint : IActionResultFilter
{
public async Task Invoke(ActionResultFilterContext context, Func<Task> next)
{
await context.Result.ExecuteResultAsync(context.ActionContext);
}
}
}

View File

@ -6,7 +6,7 @@ namespace Microsoft.AspNet.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class AuthorizationFilterAttribute : Attribute, IFilter, IAuthorizationFilter
{
public abstract Task Invoke(AuthorizationFilterContext context, Func<AuthorizationFilterContext, Task> next);
public abstract Task Invoke(AuthorizationFilterContext context, Func<Task> next);
public int Order { get; set; }
}

View File

@ -0,0 +1,17 @@
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Filters
{
public class AuthorizationFilterEndPoint : IAuthorizationFilter
{
public bool EndPointCalled { get; private set; }
public Task Invoke(AuthorizationFilterContext context, Func<Task> next)
{
EndPointCalled = true;
return Task.FromResult(true);
}
}
}

View File

@ -6,7 +6,7 @@ namespace Microsoft.AspNet.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ExceptionFilterAttribute : Attribute, IExceptionFilter, IFilter
{
public abstract Task Invoke(ExceptionFilterContext context, Func<ExceptionFilterContext, Task> next);
public abstract Task Invoke(ExceptionFilterContext context, Func<Task> next);
public int Order { get; set; }
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Filters
{
public class FilterPipelineBuilder<T>
{
private readonly IFilter<T>[] _filters;
private readonly T _context;
// Filters are already ordered externally.
public FilterPipelineBuilder(IEnumerable<IFilter<T>> filters, T context)
{
_filters = filters.ToArray();
_context = context;
}
public async Task InvokeAsync()
{
var caller = new CallNextAsync(_context, _filters);
await caller.CallNextProvider();
}
private class CallNextAsync
{
private readonly T _context;
private readonly IFilter<T>[] _filters;
private readonly Func<Task> _next;
private int _index;
public CallNextAsync(T context, IFilter<T>[] filters)
{
_context = context;
_next = CallNextProvider;
_filters = filters;
}
public async Task CallNextProvider()
{
if (_filters.Length > _index)
{
await _filters[_index++].Invoke(_context, _next);
}
}
}
}
}

View File

@ -5,6 +5,6 @@ namespace Microsoft.AspNet.Mvc.Filters
{
public interface IFilter<T>
{
Task Invoke(T context, Func<T, Task> next);
Task Invoke(T context, Func<Task> next);
}
}

View File

@ -7,6 +7,8 @@ using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.DependencyInjection.NestedProviders;
using Microsoft.AspNet.Mvc.Filters;
namespace Microsoft.AspNet.Mvc
{
@ -41,6 +43,8 @@ namespace Microsoft.AspNet.Mvc
public async Task InvokeActionAsync()
{
IActionResult actionResult;
var context = new FilterProviderContext(_descriptor);
_filterProvider.Invoke(context);
var modelState = new ModelStateDictionary();
object controller = _controllerFactory.CreateController(_actionContext, modelState);
@ -61,18 +65,53 @@ namespace Microsoft.AspNet.Mvc
{
var parameterValues = await GetParameterValues(modelState);
var context = new FilterProviderContext(_descriptor);
_filterProvider.Invoke(context);
var authZFilters = context.AuthorizationFilters;
var authZEndPoint = new AuthorizationFilterEndPoint();
authZFilters.Add(authZEndPoint);
var authZContext = new AuthorizationFilterContext(_actionContext);
var authZPipeline = new FilterPipelineBuilder<AuthorizationFilterContext>(authZFilters, authZContext);
object actionReturnValue = method.Invoke(controller, GetArgumentValues(parameterValues));
await authZPipeline.InvokeAsync();
if (authZContext.ActionResult == null &&
!authZContext.HasFailed &&
authZEndPoint.EndPointCalled)
{
var actionFilters = context.ActionFilters;
var actionFilterContext = new ActionFilterContext(_actionContext,
new Dictionary<string, object>());
actionResult = _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _actionContext);
// TODO: This is extremely temporary and is going to get soon replaced with the action executer
var actionEndPoint = new ReflectedActionFilterEndPoint(async (inArray) => method.Invoke(controller, inArray),
_actionResultFactory);
actionFilters.Add(actionEndPoint);
var actionFilterPipeline = new FilterPipelineBuilder<ActionFilterContext>(actionFilters,
actionFilterContext);
await actionFilterPipeline.InvokeAsync();
object actionReturnValue = method.Invoke(controller, null);
actionResult = _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _actionContext);
}
else
{
actionResult = authZContext.ActionResult ?? new HttpStatusCodeResult(401);
}
}
}
// TODO: This will probably move out once we got filters
var actionResultFilters = context.ActionResultFilters;
await actionResult.ExecuteResultAsync(_actionContext);
var actionResultFilterContext = new ActionResultFilterContext(_actionContext, actionResult);
var actionResultFilterEndPoint = new ActionResultFilterEndPoint();
actionResultFilters.Add(actionResultFilterEndPoint);
var actionResultPipeline = new FilterPipelineBuilder<ActionResultFilterContext>(actionResultFilters, actionResultFilterContext);
await actionResultPipeline.InvokeAsync();
}
private async Task<IDictionary<string, object>> GetParameterValues(ModelStateDictionary modelState)