Default filter provider, instantiates and merges all filters pre action invocation.

This commit is contained in:
Yishai Galatzer 2014-03-03 04:25:27 -08:00
parent 4bb66937e3
commit 4d90d590a9
5 changed files with 219 additions and 0 deletions

View File

@ -1,4 +1,5 @@
using System;
using Microsoft.AspNet.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
@ -8,15 +9,19 @@ namespace Microsoft.AspNet.Mvc
private readonly IServiceProvider _serviceProvider;
private readonly IControllerFactory _controllerFactory;
private readonly IActionBindingContextProvider _bindingProvider;
private readonly INestedProviderManager<FilterProviderContext> _filterProvider;
public ActionInvokerProvider(IActionResultFactory actionResultFactory,
IControllerFactory controllerFactory,
IActionBindingContextProvider bindingProvider,
INestedProviderManager<FilterProviderContext> filterProvider,
IServiceProvider serviceProvider)
{
_actionResultFactory = actionResultFactory;
_controllerFactory = controllerFactory;
_bindingProvider = bindingProvider;
_filterProvider = filterProvider;
_serviceProvider = serviceProvider;
}
@ -37,6 +42,7 @@ namespace Microsoft.AspNet.Mvc
_actionResultFactory,
_controllerFactory,
_bindingProvider,
_filterProvider,
_serviceProvider);
}

View File

@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
public class DefaultFilterProvider : INestedProvider<FilterProviderContext>
{
public DefaultFilterProvider(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public virtual void Invoke(FilterProviderContext context, Action callNext)
{
List<IFilter> filters = context.ActionDescriptor.Filters;
if (filters == null)
{
filters = new List<IFilter>();
}
else
{
filters = filters.ToList(); // make a copy of the list, TODO: Make the actiondescriptor immutable
}
AddGlobalFilters(filters);
if (filters.Count > 0)
{
for (int i = 0; i < filters.Count; i++)
{
GetFilter(context, filters[i]);
}
}
if (callNext != null)
{
callNext();
}
}
public virtual void GetFilter(FilterProviderContext context, IFilter filter)
{
var serviceFilterSignature = filter as IServiceFilter;
if (serviceFilterSignature != null)
{
var serviceFilter = ServiceProvider.GetService(serviceFilterSignature.ServiceType);
AddFilters(context, serviceFilter, true);
// if the filter implements more than the just IServiceFilter
AddFilters(context, filter, false);
}
else
{
AddFilters(context, filter, true);
}
}
public virtual List<IFilter> AddGlobalFilters(List<IFilter> filters)
{
var globalFilters = ServiceProvider.GetService<IEnumerable<IFilter>>().AsArray();
if (globalFilters == null || globalFilters.Length == 0)
{
return filters;
}
return MergeSorted(filters, globalFilters);
}
private List<IFilter> MergeSorted(List<IFilter> filtersFromAction, IFilter[] globalFilters)
{
if (globalFilters.Length == 0)
{
return filtersFromAction;
}
var list = new List<IFilter>();
var count = filtersFromAction.Count + globalFilters.Length;
for (int i = 0, j = 0; i + j < count; )
{
if (i >= filtersFromAction.Count)
{
list.Add(globalFilters[j++]);
}
else if (j >= globalFilters.Length)
{
list.Add(filtersFromAction[i++]);
}
else if (filtersFromAction[i].Order >= globalFilters[j].Order)
{
list.Add(filtersFromAction[i++]);
}
else
{
list.Add(globalFilters[j++]);
}
}
return list;
}
protected IServiceProvider ServiceProvider { get; private set; }
public int Order
{
get { return 0; }
}
private void AddFilters(FilterProviderContext context, object filter, bool throwIfNotFilter)
{
bool shouldThrow = throwIfNotFilter;
var authFilter = filter as IAuthorizationFilter;
var actionFilter = filter as IActionFilter;
var actionResultFilter = filter as IActionResultFilter;
var exceptionFilter = filter as IExceptionFilter;
if (authFilter != null)
{
if (context.AuthorizationFilters == null)
{
context.AuthorizationFilters = new List<IAuthorizationFilter>();
}
context.AuthorizationFilters.Add(authFilter);
shouldThrow = false;
}
if (actionFilter != null)
{
if (context.ActionFilters == null)
{
context.ActionFilters = new List<IActionFilter>();
}
context.ActionFilters.Add(actionFilter);
shouldThrow = false;
}
if (actionResultFilter != null)
{
if (context.ActionResultFilters == null)
{
context.ActionResultFilters = new List<IActionResultFilter>();
}
context.ActionResultFilters.Add(actionResultFilter);
shouldThrow = false;
}
if (exceptionFilter != null)
{
if (context.ExceptionFilters == null)
{
context.ExceptionFilters = new List<IExceptionFilter>();
}
context.ExceptionFilters.Add(exceptionFilter);
shouldThrow = false;
}
if (shouldThrow)
{
throw new InvalidOperationException("Filter has to be IActionResultFilter, IActionFilter, IExceptionFilter or IAuthorizationFilter.");
}
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc
{
public class FilterProviderContext
{
public FilterProviderContext(ActionDescriptor actionDescriptor)
{
ActionDescriptor = actionDescriptor;
}
// Input
public ActionDescriptor ActionDescriptor { get; set; }
// Results
public List<IAuthorizationFilter> AuthorizationFilters { get; set; }
public List<IActionFilter> ActionFilters { get; set; }
public List<IActionResultFilter> ActionResultFilters { get; set; }
public List<IExceptionFilter> ExceptionFilters { get; set; }
}
}

View File

@ -5,6 +5,7 @@ using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc
@ -17,12 +18,14 @@ namespace Microsoft.AspNet.Mvc
private readonly IServiceProvider _serviceProvider;
private readonly IControllerFactory _controllerFactory;
private readonly IActionBindingContextProvider _bindingProvider;
private readonly INestedProviderManager<FilterProviderContext> _filterProvider;
public ReflectedActionInvoker(ActionContext actionContext,
ReflectedActionDescriptor descriptor,
IActionResultFactory actionResultFactory,
IControllerFactory controllerFactory,
IActionBindingContextProvider bindingContextProvider,
INestedProviderManager<FilterProviderContext> filterProvider,
IServiceProvider serviceProvider)
{
_actionContext = actionContext;
@ -30,6 +33,8 @@ namespace Microsoft.AspNet.Mvc
_actionResultFactory = actionResultFactory;
_controllerFactory = controllerFactory;
_bindingProvider = bindingContextProvider;
_filterProvider = filterProvider;
_serviceProvider = serviceProvider;
}
@ -56,8 +61,12 @@ namespace Microsoft.AspNet.Mvc
{
var parameterValues = await GetParameterValues(modelState);
var context = new FilterProviderContext(_descriptor);
_filterProvider.Invoke(context);
object actionReturnValue = method.Invoke(controller, GetArgumentValues(parameterValues));
actionResult = _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _actionContext);
}
}

View File

@ -65,6 +65,9 @@ namespace Microsoft.AspNet.Mvc
yield return DescribeService<IModelBinder, MutableObjectModelBinder>(configuration);
yield return DescribeService<IModelBinder, ComplexModelDtoModelBinder>(configuration);
yield return DescribeService<INestedProviderManager<FilterProviderContext>, NestedProviderManager<FilterProviderContext>>(configuration);
yield return DescribeService<INestedProvider<FilterProviderContext>, DefaultFilterProvider>(configuration);
yield return DescribeService<IInputFormatter, JsonInputFormatter>(configuration);
}
@ -90,6 +93,7 @@ namespace Microsoft.AspNet.Mvc
{
var serviceTypeName = serviceType.FullName;
var implementationTypeName = configuration.Get(serviceTypeName);
if (!String.IsNullOrEmpty(implementationTypeName))
{
try