Порядок выполнения с несколькими фильтрами в веб-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

Я все еще должен исправить это для себя ??


person HelloWorld    schedule 07.02.2014    source источник
comment
Эта ссылка: strathweb.com/2012/06/ помочь?   -  person Devesh    schedule 10.04.2018


Ответы (3)


Некоторые вещи, которые следует отметить здесь:

  1. Фильтры выполняются в следующем порядке для действия: Глобально определенные фильтры -> Фильтры, специфичные для контроллера -> Фильтры, специфичные для действия.
  2. Фильтры авторизации -> Фильтры действий -> Фильтры исключений
  3. Теперь проблема, о которой вы, кажется, упоминаете, связана с наличием нескольких фильтров одного типа (например, несколько 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; }
    }
    
person Kiran Challa    schedule 07.02.2014
comment
В какой файл добавить код config.Services.Replace(...? - person Jeff Tian; 06.10.2014
comment
@KiranChalla А как насчет глобально определенных фильтров действий? - person lbrahim; 05.11.2014
comment
Он выполняет глобально определенный фильтр независимо от того, какая коллекция заказана. Во всяком случае вокруг этого? - person lbrahim; 05.11.2014
comment
И еще раз, куда девается код config.Services.Replace(...? - person freakinthesun; 21.08.2015
comment
@freakinthesun: вызов config.Services.Replace поступает в ваш Startup, если вы используете хостинг с конвейером OWIN, или в Global.asax.cs, если нет. - person Tomas Aschan; 07.12.2015
comment
У меня был другой ответ, обеспечивающий некоторые улучшения. Не стесняйтесь взглянуть. - person dknaack; 01.03.2016
comment
@MIKE У меня та же проблема - она ​​вызвана вызовом config.Services.Replace, но сейчас у меня нет решения. - person Dai; 03.09.2016
comment
Для справки, порядок фильтров по адресу: tutorialspoint.com/asp.net_mvc/asp. net_mvc_filters.htm - person Abacus; 05.04.2019

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

Проблема была в методе

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, как атрибут IOrderFilter с порядковым номером 0, и объединить его с атрибутами IOrderFilter, упорядочить и вернуть их.

Второе решение лучше, потому что оно позволяет мне поставить свой атрибут IOrderFilter перед сторонними атрибутами, которые не реализуют IOrderFilter.

Образец

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

Так что казнь будет

  • ЗаказываемыйАтрибутА
  • NonOrderableThirdPartyAttribute
  • ЗаказываемыйАтрибутB
  • ЗаказываемыйАтрибутC

Итак, вот измененный код

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);
    }
}
person dknaack    schedule 01.03.2016

Если у вас есть несколько фильтров одного типа, порядок выполнения будет Глобальный -> Контроллер -> Действие.

А для фильтра авторизации, если вы установите несколько фильтров на разных уровнях, они будут объединены с помощью «И» и будут рассчитываться в указанном выше порядке выполнения.

И процесс авторизации завершится ошибкой при первом неудавшемся фильтре.

Для получения более подробной информации вы можете проверить этот пост.

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

person Shawn Teng    schedule 12.09.2018