Kolejność wykonania z wieloma filtrami w web api

Używam najnowszego web api.

Dodaję niektóre kontrolery z 3 różnymi atrybutami filtrów.

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

Nie mogę być pewien, czy filtry działają w kolejności, w jakiej są deklarowane od góry do dołu.

Jak określić kolejność wykonania w 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

Czy nadal muszę to naprawić ??
Author: HelloWorld, 2014-02-07

3 answers

Kilka rzeczy do odnotowania tutaj:

  1. filtry są wykonywane w następującej kolejności dla akcji: globalnie Definiowane Filtry - > Filtry specyficzne dla kontrolera- > filtry specyficzne dla akcji.
  2. Filtry Autoryzacyjne - > Filtry Akcji - > Wyjątek Filtry
  3. Teraz problem, o którym wspominasz, jest związany z posiadające wiele filtrów tego samego rodzaju (np. ActionFilterAttribute dekorowane na kontroler lub działanie. Jest to przypadek, który nie gwarancja porządek jako jego opiera się na refleksji.). W tym przypadku istnieje sposób aby to zrobić w Web API przy użyciu niestandardowej implementacji System.Web.Http.Filters.IFilterProvider. Próbowałem następujących i zrobił kilka testów, aby to zweryfikować. Wygląda na to, że działa dobrze. Możesz spróbować i sprawdzić, czy działa zgodnie z oczekiwaniami.

    // 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; }
    }
    
 59
Author: Kiran Challa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-13 12:35:24

Miałem pewne problemy z rozwiązaniem z odpowiedzi Kiran Challa. Oto moja modyfikacja.

Problem tkwił w metodzie

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

Jak widać zwracane będą tylko filtry implementujące IOrderedFilter. Miałem atrybut strony trzeciej, który zostaje wycięty i w rezultacie nie jest wykonywany.

Więc miałem dwa możliwe rozwiązania.

  1. użyj dziedziczenia, aby utworzyć rozszerzoną wersję atrybutu third party, aby zaimplementować IOrderFilter też.
  2. Zmień metodę traktowania każdego atrybutu, który nie implementuje IOrderFilter Jak atrybut IOrderFilter o numerze porządkowym 0 i połącz go z atrybutami IOrderFilter, Uporządkuj i zwróć je.

Drugie rozwiązanie jest lepsze, ponieważ pozwala mi przenieść mój atrybut IOrderFilter przed atrybuty stron trzecich, które nie implementują IOrderFilter.

Próbka

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

Więc egzekucja byłaby

  • OrderableAttributeA
  • NonOrderableThirdPartyAttribute
  • OrderableAttributeB
  • OrderableAttributeC

Oto zmodyfikowany kod

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);
    }
}
 13
Author: dknaack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-01 09:36:35

Jeśli masz kilka filtrów tego samego rodzaju, kolejność wykonania będzie Global -> Controller - > Action

I dla filtra autoryzacji, jeśli ustawisz wiele filtrów na różnych poziomach, zostaną one połączone z "I" i zostaną obliczone w powyższej kolejności wykonania.

I proces autoryzacji zakończy się niepowodzeniem przy pierwszym nieudanym filtrze.

Aby uzyskać więcej informacji, możesz sprawdzić to poczta.

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

 0
Author: Shawn Teng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-12 08:54:32