web api中具有多个过滤器的执行顺序

时间:2014-02-07 13:00:00

标签: c# asp.net asp.net-web-api action-filter asp.net-web-api2

我正在使用最新的web api

我使用3种不同的过滤器属性注释一些控制器。

1 [Authorize]
2 [RessourceOwnerAttribute derived from AuthorizationFilterAttribute]
3 [InvalidModelStateAttribute derived from ActionFilterAttribute]

我无法确定过滤器是按从上到下声明的顺序运行的。

如何在web api 2.1中定义执行顺序?

https://aspnetwebstack.codeplex.com/workitem/1065#

http://aspnet.uservoice.com/forums/147201-asp-net-web-api/suggestions/3346720-execution-order-of-mvc4-webapi-action-filters

我还需要为自己解决这个问题吗?

3 个答案:

答案 0 :(得分:66)

有些事情需要注意:

  1. 过滤器按以下顺序执行操作:全局 定义的过滤器 - >控制器专用滤波器 - >特定于行动的过滤器。
  2. 授权过滤器 - >动作过滤器 - >例外 过滤器
  3. 现在你提到的问题与你有关 拥有相同类型的多个过滤器(例如:多个) ActionFilterAttribute装饰在 控制器或动作。这种情况不能保证 基于反思的顺序。对于这种情况,有一种方法 使用自定义实现在Web API中执行此操作 System.Web.Http.Filters.IFilterProvider。我尝试了以下内容 并做了一些测试来验证它。它似乎工作正常。 您可以尝试一下,看看它是否按预期工作。

    // Start clean by replacing with filter provider for global configuration.
    // For these globally added filters we need not do any ordering as filters are 
    // executed in the order they are added to the filter collection
    config.Services.Replace(typeof(IFilterProvider), new System.Web.Http.Filters.ConfigurationFilterProvider());
    
    // Custom action filter provider which does ordering
    config.Services.Add(typeof(IFilterProvider), new OrderedFilterProvider());
    

    public class OrderedFilterProvider : IFilterProvider
    {
        public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
        {
            // controller-specific
            IEnumerable<FilterInfo> controllerSpecificFilters = OrderFilters(actionDescriptor.ControllerDescriptor.GetFilters(), FilterScope.Controller);
    
            // action-specific
            IEnumerable<FilterInfo> actionSpecificFilters = OrderFilters(actionDescriptor.GetFilters(), FilterScope.Action);
    
            return controllerSpecificFilters.Concat(actionSpecificFilters);
        }
    
        private IEnumerable<FilterInfo> OrderFilters(IEnumerable<IFilter> filters, FilterScope scope)
        {
            return filters.OfType<IOrderedFilter>()
                            .OrderBy(filter => filter.Order)
                            .Select(instance => new FilterInfo(instance, scope));
        }
    }
    

    //NOTE: Here I am creating base attributes which you would need to inherit from.
    public interface IOrderedFilter : IFilter
    {
        int Order { get; set; }
    }
    
    public class ActionFilterWithOrderAttribute : ActionFilterAttribute, IOrderedFilter
    {
        public int Order { get; set; }
    }
    
    public class AuthorizationFilterWithOrderAttribute : AuthorizationFilterAttribute, IOrderedFilter
    {
        public int Order { get; set; }
    }
    
    public class ExceptionFilterWithOrderAttribute : ExceptionFilterAttribute, IOrderedFilter
    {
        public int Order { get; set; }
    }
    

答案 1 :(得分:15)

我在Kiran Challa的回答中遇到了一些问题。 这是我的修改。

问题在于方法

private IEnumerable<FilterInfo> OrderFilters(IEnumerable<IFilter> filters, FilterScope scope)
{
    return filters.OfType<IOrderedFilter>()
                    .OrderBy(filter => filter.Order)
                    .Select(instance => new FilterInfo(instance, scope));
}

正如您所见,过滤器会返回实施IOrderedFilter的过滤器。我有一个第三方属性被削减,因此没有被执行。

所以我有两种可能的解决方案。

  1. 使用继承创建第三方属性的扩展版本,以使其实现IOrderFilter
  2. 更改方法以处理未实现IOrderFilter的每个属性,例如订单号为0的IOrderFilter属性,并将其与IOrderFilter属性组合,订购并返回。
  3. 第二种解决方案更好,因为它允许我在未实现IOrderFilter的第三方属性之前使用IOrderFilter属性。

    样品

    [NonOrderableThirdPartyAttribute]
    [OrderableAttributeA(Order = -1)]
    [OrderableAttributeB(Order = 1)]
    [OrderableAttributeC(Order = 2)]
    public async Task<IHttpActionResult> Post(... request) 
    {
        // do something
    }
    

    所以执行将是

    • OrderableAttributeA
    • NonOrderableThirdPartyAttribute
    • OrderableAttributeB
    • OrderableAttributeC

    所以这里是修改后的代码

    public class OrderedFilterProvider : IFilterProvider
    {
        public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
        {
            // controller-specific
            var controllerSpecificFilters = OrderFilters(actionDescriptor.ControllerDescriptor.GetFilters(), FilterScope.Controller);
    
            // action-specific
            var actionSpecificFilters = OrderFilters(actionDescriptor.GetFilters(), FilterScope.Action);
    
            return controllerSpecificFilters.Concat(actionSpecificFilters);
        }
    
        private IEnumerable<FilterInfo> OrderFilters(IEnumerable<IFilter> filters, FilterScope scope)
        {
            // get all filter that dont implement IOrderedFilter and give them order number of 0
            var notOrderableFilter = filters.Where(f => !(f is IOrderedFilter))
                .Select(instance => new KeyValuePair<int, FilterInfo>(0, new FilterInfo(instance, scope)));
    
            // get all filter that implement IOrderFilter and give them order number from the instance
            var orderableFilter = filters.OfType<IOrderedFilter>().OrderBy(filter => filter.Order)
                .Select(instance => new KeyValuePair<int, FilterInfo>(instance.Order, new FilterInfo(instance, scope)));
    
            // concat lists => order => return
            return notOrderableFilter.Concat(orderableFilter).OrderBy(x => x.Key).Select(y => y.Value);
        }
    }
    

答案 2 :(得分:1)

如果您有多个相同类型的过滤器,则执行顺序为 全局->控制器->操作

对于授权过滤器,如果您将多个过滤器设置为不同级别,则它们将与“ AND”组合,并按上述执行顺序进行计算。

并且授权过程将在第一个失败的过滤器上失败。

有关更多详细信息,您可以查看此帖子。

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-2.1

相关问题