Move global filters addition to AD creation time

Add origin, and simplify sorting.
Add type filter
This commit is contained in:
Yishai Galatzer 2014-03-07 16:05:27 -08:00
parent 9132d32fa4
commit 7ddf8a7bdb
10 changed files with 101 additions and 113 deletions

View File

@ -18,6 +18,6 @@ namespace Microsoft.AspNet.Mvc
public List<ParameterDescriptor> Parameters { get; set; }
public List<IFilter> Filters { get; set; }
public List<FilterDescriptor> FilterDescriptors { get; set; }
}
}

View File

@ -4,7 +4,7 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Filters
{
// This one lives in the Filters namespace, and only intended to be consumed by folks that rewrite the action invoker.
// This one lives in the FilterDescriptors namespace, and only intended to be consumed by folks that rewrite the action invoker.
public class ReflectedActionFilterEndPoint : IActionFilter
{
private readonly Func<object[], Task<object>> _coreMethodInvoker;

View File

@ -3,7 +3,7 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Filters
{
// This one lives in the Filters namespace, and only intended to be consumed by folks that rewrite the action invoker.
// This one lives in the FilterDescriptors namespace, and only intended to be consumed by folks that rewrite the action invoker.
public class AuthorizationFilterEndPoint : IAuthorizationFilter
{
public bool EndPointCalled { get; private set; }

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.DependencyInjection;
namespace Microsoft.AspNet.Mvc.Filters
@ -12,28 +11,23 @@ namespace Microsoft.AspNet.Mvc.Filters
ServiceProvider = serviceProvider;
}
public virtual void Invoke(FilterProviderContext context, Action callNext)
{
List<IFilter> filters = context.ActionDescriptor.Filters;
FilterDescriptor[] filterDescriptors;
if (filters == null)
if (context.ActionDescriptor.FilterDescriptors != null)
{
filters = new List<IFilter>();
}
else
{
filters = filters.ToList(); // make a copy of the list, TODO: Make the actiondescriptor immutable
// make a copy of the list, TODO: Make the actiondescriptor immutable
filterDescriptors = context.ActionDescriptor.FilterDescriptors.ToArray();
}
//AddGlobalFilters_moveToAdPipeline(filters);
AddGlobalFilters(filters);
if (filters.Count > 0)
{
for (int i = 0; i < filters.Count; i++)
if (filterDescriptors.Length > 0)
{
GetFilter(context, filters[i]);
for (int i = 0; i < filterDescriptors.Length; i++)
{
GetFilter(context, filterDescriptors[i].Filter);
}
}
}
@ -45,66 +39,30 @@ namespace Microsoft.AspNet.Mvc.Filters
public virtual void GetFilter(FilterProviderContext context, IFilter filter)
{
bool failIfNotFilter = true;
var serviceFilterSignature = filter as IServiceFilter;
if (serviceFilterSignature != null)
{
// TODO: How do we pass extra parameters
var serviceFilter = ServiceProvider.GetService(serviceFilterSignature.ServiceType);
AddFilters(context, serviceFilter, true);
// if the filter implements more than the just IServiceFilter
AddFilters(context, filter, false);
failIfNotFilter = false;
}
else
var typeFilterSignature = filter as ITypeFilter;
if (typeFilterSignature != null)
{
AddFilters(context, filter, true);
}
}
// TODO: How do we pass extra parameters
var typeFilter =
ServiceProvider.GetService<TypeActivator>().CreateInstance(typeFilterSignature.ImplementationType);
public virtual List<IFilter> AddGlobalFilters(List<IFilter> filters)
{
var globalFilters = ServiceProvider.GetService<IEnumerable<IFilter>>().AsArray();
if (globalFilters == null || globalFilters.Length == 0)
{
return filters;
AddFilters(context, typeFilter, true);
failIfNotFilter = false;
}
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;
AddFilters(context, filter, failIfNotFilter);
}
protected IServiceProvider ServiceProvider { get; private set; }
@ -169,7 +127,7 @@ namespace Microsoft.AspNet.Mvc.Filters
if (shouldThrow)
{
throw new InvalidOperationException("Filter has to be IActionResultFilter, IActionFilter, IExceptionFilter or IAuthorizationFilter.");
throw new InvalidOperationException("Filter has to be IActionResultFilter, IActionFilter, IExceptionFilter or IAuthorizationFilter.");
}
}
}

View File

@ -0,0 +1,14 @@
namespace Microsoft.AspNet.Mvc
{
public class FilterDescriptor
{
public FilterDescriptor([NotNull]IFilter filter, int origin)
{
Filter = filter;
Origin = origin;
}
public IFilter Filter { get; private set; }
public int Origin { get; private set; }
}
}

View File

@ -0,0 +1,23 @@
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc
{
public class FilterDescriptorComparer : IComparer<FilterDescriptor>
{
private static FilterDescriptorComparer _comparer = new FilterDescriptorComparer();
public static FilterDescriptorComparer Comparer { get { return _comparer; } }
public int Compare([NotNull]FilterDescriptor x, [NotNull]FilterDescriptor y)
{
if (x.Filter.Order == y.Filter.Order)
{
return x.Origin.CompareTo(y.Origin);
}
else
{
return x.Filter.Order.CompareTo(y.Filter.Order);
}
}
}
}

View File

@ -0,0 +1,9 @@
namespace Microsoft.AspNet.Mvc
{
public static class FilterOrigin
{
public static readonly int Action = 100;
public static readonly int Controlller = 200;
public static readonly int Global = 300;
}
}

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.Filters
private readonly IFilter<T>[] _filters;
private readonly T _context;
// Filters are already ordered externally.
// FilterDescriptors are already ordered externally.
public FilterPipelineBuilder(IEnumerable<IFilter<T>> filters, T context)
{
_filters = filters.ToArray();

View File

@ -0,0 +1,9 @@
using System;
namespace Microsoft.AspNet.Mvc
{
public interface ITypeFilter : IFilter
{
Type ImplementationType { get; }
}
}

View File

@ -11,16 +11,21 @@ namespace Microsoft.AspNet.Mvc
private readonly IActionDiscoveryConventions _conventions;
private readonly IControllerDescriptorFactory _controllerDescriptorFactory;
private readonly IParameterDescriptorFactory _parameterDescriptorFactory;
private readonly IEnumerable<FilterDescriptor> _globalFilters;
public ReflectedActionDescriptorProvider(IControllerAssemblyProvider controllerAssemblyProvider,
IActionDiscoveryConventions conventions,
IControllerDescriptorFactory controllerDescriptorFactory,
IParameterDescriptorFactory parameterDescriptorFactory)
IParameterDescriptorFactory parameterDescriptorFactory,
IEnumerable<IFilter> globalFilters)
{
_controllerAssemblyProvider = controllerAssemblyProvider;
_conventions = conventions;
_controllerDescriptorFactory = controllerDescriptorFactory;
_parameterDescriptorFactory = parameterDescriptorFactory;
var filters = globalFilters ?? Enumerable.Empty<IFilter>();
_globalFilters = filters.Select(f => new FilterDescriptor(f, FilterOrigin.Global));
}
public int Order
@ -44,7 +49,12 @@ namespace Microsoft.AspNet.Mvc
foreach (var cd in controllerDescriptors)
{
var controllerAttributes = cd.ControllerTypeInfo.GetCustomAttributes(inherit: true).ToArray();
var filtersFromController = GetOrderedFilterAttributes(controllerAttributes);
var globalAndControllerFilters =
controllerAttributes.OfType<IFilter>()
.Select(filter => new FilterDescriptor(filter, FilterOrigin.Controlller))
.Concat(_globalFilters)
.OrderBy(d => d, FilterDescriptorComparer.Comparer)
.ToArray();
foreach (var methodInfo in cd.ControllerTypeInfo.DeclaredMethods)
{
@ -57,23 +67,16 @@ namespace Microsoft.AspNet.Mvc
foreach (var actionInfo in actionInfos)
{
yield return BuildDescriptor(cd, methodInfo, actionInfo, filtersFromController);
yield return BuildDescriptor(cd, methodInfo, actionInfo, globalAndControllerFilters);
}
}
}
}
private IFilter[] GetOrderedFilterAttributes(object[] attributes)
{
var filters = attributes.OfType<IFilter>().OrderByDescending(filter => filter.Order);
return filters.ToArray();
}
private ReflectedActionDescriptor BuildDescriptor(ControllerDescriptor controllerDescriptor,
MethodInfo methodInfo,
ActionInfo actionInfo,
IFilter[] controllerFilters)
FilterDescriptor[] globalAndControllerFilters)
{
var ad = new ReflectedActionDescriptor
{
@ -110,41 +113,13 @@ namespace Microsoft.AspNet.Mvc
var attributes = methodInfo.GetCustomAttributes(inherit: true).ToArray();
// TODO: add ordering support such that action filters are ahead of controller filters if they have the same order
var filtersFromAction = GetOrderedFilterAttributes(attributes);
var filtersFromAction = attributes.OfType<IFilter>().Select(filter => new FilterDescriptor(filter, FilterOrigin.Action));
ad.Filters = MergeSorted(filtersFromAction, controllerFilters);
ad.FilterDescriptors = filtersFromAction.Concat(globalAndControllerFilters)
.OrderBy(d => d, FilterDescriptorComparer.Comparer)
.ToList();
return ad;
}
// Merge filters, filters with the same order are prioritzed by origin action executes ahead of controller.
private List<IFilter> MergeSorted(IFilter[] filtersFromAction, IFilter[] filtersFromController)
{
var list = new List<IFilter>();
var count = filtersFromAction.Length + filtersFromController.Length;
for (int i = 0, j = 0; i + j < count; )
{
if (i >= filtersFromAction.Length)
{
list.Add(filtersFromController[j++]);
}
else if (j >= filtersFromController.Length)
{
list.Add(filtersFromAction[i++]);
}
else if (filtersFromAction[i].Order >= filtersFromController[j].Order)
{
list.Add(filtersFromAction[i++]);
}
else
{
list.Add(filtersFromController[j++]);
}
}
return list;
}
}
}