Adding controller-as-filter support

If the controller implements IFilter, it will be added to the filters
collection.

It's hardcoded to be 'first' as it was in MVC, but can be overridden by
implementing IOrderedFilter.
This commit is contained in:
Ryan Nowak 2014-04-30 16:38:56 -07:00
parent a9a7663cbd
commit 7a8dc36553
6 changed files with 84 additions and 13 deletions

View File

@ -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");

View File

@ -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<FilterItem>
{
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);
}
}
}

View File

@ -21,16 +21,16 @@ namespace Microsoft.AspNet.Mvc
{
public class FilterProviderContext
{
public FilterProviderContext(ActionDescriptor actionDescriptor, List<FilterItem> items)
public FilterProviderContext([NotNull] ActionContext actionContext, [NotNull] List<FilterItem> items)
{
ActionDescriptor = actionDescriptor;
Result = items;
ActionContext = actionContext;
Results = items;
}
// Input
public ActionDescriptor ActionDescriptor { get; set; }
public ActionContext ActionContext { get; set; }
// Result
public List<FilterItem> Result { get; set; }
// Results
public List<FilterItem> Results { get; set; }
}
}

View File

@ -73,6 +73,7 @@
<Compile Include="DefaultControllerFactory.cs" />
<Compile Include="DefaultParameterDescriptorFactory.cs" />
<Compile Include="Extensions\IEnumerableExtensions.cs" />
<Compile Include="Filters\FilterItemOrderComparer.cs" />
<Compile Include="Filters\TypeFilterAttribute.cs" />
<Compile Include="Filters\ActionExecutedContext.cs" />
<Compile Include="Filters\ActionExecutingContext.cs" />

View File

@ -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()

View File

@ -1255,7 +1255,7 @@ namespace Microsoft.AspNet.Mvc
filterProvider
.Setup(fp => fp.Invoke(It.IsAny<FilterProviderContext>()))
.Callback<FilterProviderContext>(
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,