Filter discovery

This commit is contained in:
Yishai Galatzer 2014-03-03 01:49:55 -08:00
parent dc6b1b1a4a
commit 75bccbae21
5 changed files with 100 additions and 3 deletions

View File

@ -0,0 +1,21 @@
using Microsoft.AspNet.Mvc;
using MvcSample.Models;
namespace MvcSample
{
// Expected order in descriptor - object -> int -> string
// TODO: Add a real filter here
[ServiceFilter(typeof(object), Order = 1)]
[ServiceFilter(typeof(string))]
public class FiltersController : Controller
{
private readonly User _user = new User() { Name = "User Name", Address = "Home Address" };
// TODO: Add a real filter here
[ServiceFilter(typeof(int))]
public IActionResult Index()
{
return View("MyView", _user);
}
}
}

View File

@ -3,5 +3,6 @@ namespace Microsoft.AspNet.Mvc
public interface IFilter
{
// Marker only interface to any IFilter gets picked up by the DefaultActionDescriptorProvider
int Order { get; }
}
}

View File

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

View File

@ -0,0 +1,19 @@
using System;
using System.Diagnostics;
namespace Microsoft.AspNet.Mvc
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
[DebuggerDisplay("ServiceFilter: Type={ServiceType} Order={Order}")]
public class ServiceFilterAttribute : Attribute, IServiceFilter
{
public ServiceFilterAttribute(Type type)
{
ServiceType = type;
}
public Type ServiceType { get; private set; }
public int Order { get; set; }
}
}

View File

@ -43,6 +43,8 @@ namespace Microsoft.AspNet.Mvc
foreach (var cd in controllerDescriptors)
{
var controllerFilters = GetOrderedFilterAttributes(cd.ControllerTypeInfo);
foreach (var methodInfo in cd.ControllerTypeInfo.DeclaredMethods)
{
var actionInfos = _conventions.GetActions(methodInfo);
@ -54,13 +56,24 @@ namespace Microsoft.AspNet.Mvc
foreach (var actionInfo in actionInfos)
{
yield return BuildDescriptor(cd, methodInfo, actionInfo);
yield return BuildDescriptor(cd, methodInfo, actionInfo, controllerFilters);
}
}
}
}
private ReflectedActionDescriptor BuildDescriptor(ControllerDescriptor controllerDescriptor, MethodInfo methodInfo, ActionInfo actionInfo)
private IFilter[] GetOrderedFilterAttributes(MemberInfo memberInfo)
{
var attributes = memberInfo.GetCustomAttributes(inherit: true);
var filters = attributes.OfType<IFilter>().OrderByDescending(filter => filter.Order);
return filters.ToArray();
}
private ReflectedActionDescriptor BuildDescriptor(ControllerDescriptor controllerDescriptor,
MethodInfo methodInfo,
ActionInfo actionInfo,
IFilter[] controllerFilters)
{
var ad = new ReflectedActionDescriptor
{
@ -85,7 +98,7 @@ namespace Microsoft.AspNet.Mvc
if (actionInfo.RequireActionNameMatch)
{
ad.RouteConstraints.Add(new RouteDataActionConstraint("action", actionInfo.ActionName));
ad.RouteConstraints.Add(new RouteDataActionConstraint("action", actionInfo.ActionName));
}
else
{
@ -94,7 +107,41 @@ namespace Microsoft.AspNet.Mvc
ad.Parameters = methodInfo.GetParameters().Select(p => _parameterDescriptorFactory.GetDescriptor(p)).ToList();
// TODO: add ordering support such that action filters are ahead of controller filters if they have the same order
var actionFilters = GetOrderedFilterAttributes(methodInfo);
ad.Filters = MergeSorted(actionFilters, controllerFilters);
return ad;
}
internal List<IFilter> MergeSorted(IFilter[] actionFilters, IFilter[] controllerFilters)
{
var list = new List<IFilter>();
var count = actionFilters.Length + controllerFilters.Length;
for (int i = 0, j = 0; i + j < count; )
{
if (i >= actionFilters.Length)
{
list.Add(controllerFilters[j++]);
}
else if (j >= controllerFilters.Length)
{
list.Add(actionFilters[i++]);
}
else if (actionFilters[i].Order >= controllerFilters[j].Order)
{
list.Add(actionFilters[i++]);
}
else
{
list.Add(controllerFilters[j++]);
}
}
return list;
}
}
}