diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionDescriptor.cs b/src/Microsoft.AspNet.Mvc.Core/ActionDescriptor.cs index 2f1895fa1d..f45181f999 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionDescriptor.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionDescriptor.cs @@ -18,6 +18,6 @@ namespace Microsoft.AspNet.Mvc public List Parameters { get; set; } - public List Filters { get; set; } + public List FilterDescriptors { get; set; } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/ActionFilterEndPoint.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/ActionFilterEndPoint.cs index d14b40273c..bdc141be8b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/ActionFilterEndPoint.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/ActionFilterEndPoint.cs @@ -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> _coreMethodInvoker; diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/AuthorizationFilterEndPoint.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/AuthorizationFilterEndPoint.cs index 910cfe0479..73f8656480 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/AuthorizationFilterEndPoint.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/AuthorizationFilterEndPoint.cs @@ -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; } diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultFilterProvider.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultFilterProvider.cs index 0fa858f778..53c285becc 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultFilterProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultFilterProvider.cs @@ -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 filters = context.ActionDescriptor.Filters; + FilterDescriptor[] filterDescriptors; - if (filters == null) + if (context.ActionDescriptor.FilterDescriptors != null) { - filters = new List(); - } - 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().CreateInstance(typeFilterSignature.ImplementationType); - public virtual List AddGlobalFilters(List filters) - { - var globalFilters = ServiceProvider.GetService>().AsArray(); - - if (globalFilters == null || globalFilters.Length == 0) - { - return filters; + AddFilters(context, typeFilter, true); + failIfNotFilter = false; } - return MergeSorted(filters, globalFilters); - } - - private List MergeSorted(List filtersFromAction, IFilter[] globalFilters) - { - if (globalFilters.Length == 0) - { - return filtersFromAction; - } - - var list = new List(); - - 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."); } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/FilterDescriptor.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterDescriptor.cs new file mode 100644 index 0000000000..c6d5fb03a7 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterDescriptor.cs @@ -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; } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/FilterDescriptorComparer.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterDescriptorComparer.cs new file mode 100644 index 0000000000..4f5817b0d7 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterDescriptorComparer.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Microsoft.AspNet.Mvc +{ + public class FilterDescriptorComparer : IComparer + { + 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); + } + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/FilterOrigin.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterOrigin.cs new file mode 100644 index 0000000000..416fc51f4f --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterOrigin.cs @@ -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; + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/FilterPipelineBuilder.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterPipelineBuilder.cs index f0cb174d82..a8e9b34486 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/FilterPipelineBuilder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterPipelineBuilder.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.Filters private readonly IFilter[] _filters; private readonly T _context; - // Filters are already ordered externally. + // FilterDescriptors are already ordered externally. public FilterPipelineBuilder(IEnumerable> filters, T context) { _filters = filters.ToArray(); diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/ITypeFilter.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/ITypeFilter.cs new file mode 100644 index 0000000000..08f1d4b7a2 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/ITypeFilter.cs @@ -0,0 +1,9 @@ +using System; + +namespace Microsoft.AspNet.Mvc +{ + public interface ITypeFilter : IFilter + { + Type ImplementationType { get; } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionDescriptorProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionDescriptorProvider.cs index 2ff8f02b1c..a890b965d4 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionDescriptorProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionDescriptorProvider.cs @@ -11,16 +11,21 @@ namespace Microsoft.AspNet.Mvc private readonly IActionDiscoveryConventions _conventions; private readonly IControllerDescriptorFactory _controllerDescriptorFactory; private readonly IParameterDescriptorFactory _parameterDescriptorFactory; + private readonly IEnumerable _globalFilters; public ReflectedActionDescriptorProvider(IControllerAssemblyProvider controllerAssemblyProvider, IActionDiscoveryConventions conventions, IControllerDescriptorFactory controllerDescriptorFactory, - IParameterDescriptorFactory parameterDescriptorFactory) + IParameterDescriptorFactory parameterDescriptorFactory, + IEnumerable globalFilters) { _controllerAssemblyProvider = controllerAssemblyProvider; _conventions = conventions; _controllerDescriptorFactory = controllerDescriptorFactory; _parameterDescriptorFactory = parameterDescriptorFactory; + var filters = globalFilters ?? Enumerable.Empty(); + + _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() + .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().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().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 MergeSorted(IFilter[] filtersFromAction, IFilter[] filtersFromController) - { - var list = new List(); - - 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; - } } }