Подтвердить что ты не робот

Порядок выполнения с несколькими фильтрами в web api

Я использую последние web api.

Я комментирую некоторые контроллеры с тремя различными атрибутами фильтра.

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

Должен ли я все-таки исправить это для себя?

4b9b3361

Ответ 1

Некоторые примечания здесь:

  • Фильтры выполняются в следующем порядке для действия: глобально Определенные фильтры → Фильтры, специфичные для контроллера, → Фильтры, зависящие от конкретных действий.
  • Фильтры авторизации → Фильтры действий → Исключение Фильтры
  • Теперь проблема, о которой вы говорите, связана с с несколькими фильтрами одного типа (например: несколько ActionFilterAttribute украшен контроллер или действие. Это тот случай, который не гарантировал бы порядок, основанный на отражении.). Для этого случая есть способ сделать это в веб-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; }
    }
    

Ответ 2

У меня возникли проблемы с решением ответа Кирана Шаллы. Вот моя модификация.

Проблема заключалась в методе

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

Как видите, будут возвращены фильтры только, которые реализуют IOrderedFilter. У меня был сторонний атрибут, который обрезается и в результате не выполняется.

Итак, у меня было два возможных решения.

  • Используйте наследование для создания расширенной версии стороннего атрибута, чтобы он также реализовал IOrderFilter.
  • Измените метод для обработки каждого атрибута, который не реализует IOrderFilter как атрибут IOrderFilter с порядковым номером 0 и объединяет его с атрибутами IOrderFilter, заказывает и возвращает их.

Второе решение лучше, потому что оно позволяет мне принести мой атрибут 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);
    }
}