diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultFilterProvider.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultFilterProvider.cs index 4c04eaf696..f612f3f61c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultFilterProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/DefaultFilterProvider.cs @@ -41,14 +41,20 @@ namespace Microsoft.AspNet.Mvc.Filters public virtual void Invoke(FilterProviderContext context, Action callNext) { - if (context.ActionDescriptor.FilterDescriptors != null) + if (context.ActionContext.ActionDescriptor.FilterDescriptors != null) { - foreach (var item in context.Result) + foreach (var item in context.Results) { ProvideFilter(context, item); } } + var controllerFilter = context.ActionContext.Controller as IFilter; + if (controllerFilter != null) + { + InsertControllerAsFilter(context, controllerFilter); + } + if (callNext != null) { callNext(); @@ -84,6 +90,37 @@ namespace Microsoft.AspNet.Mvc.Filters } } + private void InsertControllerAsFilter(FilterProviderContext context, IFilter controllerFilter) + { + // If the controller implements a filter, and doesn't specify order, then it should + // run closest to the action. + int order = Int32.MaxValue; + var orderedControllerFilter = controllerFilter as IOrderedFilter; + if (orderedControllerFilter == null) + { + order = orderedControllerFilter.Order; + } + + var descriptor = new FilterDescriptor(controllerFilter, FilterScope.Controller); + var item = new FilterItem(descriptor, controllerFilter); + + // BinarySearch will return the index of where the item _should_be_ in the list. + // + // If index > 0: + // Other items in the list have the same order and scope - the item was 'found'. + // + // If index < 0: + // No other items in the list have the same order and scope - the item was not 'found' + // Index will be the bitwise compliment of of the 'right' location. + var index = context.Results.BinarySearch(item, FilterItemOrderComparer.Comparer); + if (index < 0) + { + index = ~index; + } + + context.Results.Insert(index, item); + } + private void ApplyFilterToContainer(object actualFilter, IFilter filterMetadata) { Contract.Assert(actualFilter != null, "actualFilter should not be null"); diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/FilterItemOrderComparer.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterItemOrderComparer.cs new file mode 100644 index 0000000000..ebfa7ce79f --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterItemOrderComparer.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Open Technologies, Inc. +// All Rights Reserved +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING +// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF +// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR +// NON-INFRINGEMENT. +// See the Apache 2 License for the specific language governing +// permissions and limitations under the License. + +using System.Collections.Generic; + +namespace Microsoft.AspNet.Mvc +{ + public class FilterItemOrderComparer : IComparer + { + private static readonly FilterItemOrderComparer _comparer = new FilterItemOrderComparer(); + + public static FilterItemOrderComparer Comparer { get { return _comparer; } } + + public int Compare([NotNull] FilterItem x, [NotNull] FilterItem y) + { + return FilterDescriptorOrderComparer.Comparer.Compare(x.Descriptor, y.Descriptor); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/FilterProviderContext.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterProviderContext.cs index 4b16870f83..34e0c99d5a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/FilterProviderContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/FilterProviderContext.cs @@ -21,16 +21,16 @@ namespace Microsoft.AspNet.Mvc { public class FilterProviderContext { - public FilterProviderContext(ActionDescriptor actionDescriptor, List items) + public FilterProviderContext([NotNull] ActionContext actionContext, [NotNull] List items) { - ActionDescriptor = actionDescriptor; - Result = items; + ActionContext = actionContext; + Results = items; } // Input - public ActionDescriptor ActionDescriptor { get; set; } + public ActionContext ActionContext { get; set; } - // Result - public List Result { get; set; } + // Results + public List Results { get; set; } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj b/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj index 6ac42e8f14..c99bf0c57a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj +++ b/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj @@ -73,6 +73,7 @@ + diff --git a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs index dbc8937fc9..8964f8fd82 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs @@ -74,11 +74,11 @@ namespace Microsoft.AspNet.Mvc public async Task InvokeActionAsync() { + _actionContext.Controller = _controllerFactory.CreateController(_actionContext); + _filters = GetFilters(); _cursor = new FilterCursor(_filters); - _actionContext.Controller = _controllerFactory.CreateController(_actionContext); - // >> ExceptionFilters >> AuthorizationFilters >> ActionFilters >> Action await InvokeActionExceptionFilters(); @@ -116,12 +116,12 @@ namespace Microsoft.AspNet.Mvc private IFilter[] GetFilters() { var filterProviderContext = new FilterProviderContext( - _descriptor, + _actionContext, _descriptor.FilterDescriptors.Select(fd => new FilterItem(fd)).ToList()); _filterProvider.Invoke(filterProviderContext); - return filterProviderContext.Result.Select(item => item.Filter).Where(filter => filter != null).ToArray(); + return filterProviderContext.Results.Select(item => item.Filter).Where(filter => filter != null).ToArray(); } private async Task InvokeActionExceptionFilters() diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs index 6ef4c03ee6..eb363fc6aa 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs @@ -1255,7 +1255,7 @@ namespace Microsoft.AspNet.Mvc filterProvider .Setup(fp => fp.Invoke(It.IsAny())) .Callback( - context => context.Result.AddRange(filters.Select(f => new FilterItem(null, f)))); + context => context.Results.AddRange(filters.Select(f => new FilterItem(null, f)))); var invoker = new ReflectedActionInvoker( actionContext,