Orden de ejecución con múltiples filtros en web api


Estoy usando la última web api.

Anoto algunos controladores con 3 atributos de filtro diferentes.

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

No puedo estar seguro de que los filtros se ejecuten en el orden en que se declaran de arriba a abajo.

¿Cómo defino el orden de ejecución en 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

¿Todavía tengo que arreglar eso por mí mismo ??

Author: HelloWorld, 2014-02-07

3 answers

Algunas cosas a tener en cuenta aquí:

  1. Los filtros se ejecutan en el siguiente orden para una acción: Globalmente Filtros definidos - > Filtros específicos del Controlador - > Filtros específicos de la acción.
  2. Filtros de autorización - > Filtros de acción - > Excepción Filtros
  3. Ahora el problema que parece mencionar está relacionado con tener múltiples filtros del mismo tipo (ej: Múltiples ActionFilterAttribute decorado en un controlador o una acción. Este es el caso que no garantizar el orden se basa en la reflexión.). Para este caso, hay una manera para hacerlo en Web API utilizando la implementación personalizada de System.Web.Http.Filters.IFilterProvider. He intentado lo siguiente e hice algunas pruebas para verificarlo. Parece que funciona bien. Puedes intentarlo y ver si funciona como esperabas.

    // 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-13 12:35:24

Tuve algunos problemas con la solución de Kiran Challa 's respuesta. Aquí está mi modificación.

, El problema estaba en el método

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

Como puede ver solo se devolverán los filtros que implementen IOrderedFilter. Tenía un atributo de terceros que se corta y, como resultado, no se ejecuta.

Así que tenía dos posibles soluciones.

  1. Use la herencia para crear una versión extendida del atributo de terceros con el fin de que implemente IOrderFilter demasiado.
  2. Cambie el método para tratar cada atributo que no implemente IOrderFilter como un atributo IOrderFilter con el número de orden de 0 y combinarlo con los atributos IOrderFilter, ordenarlos y devolverlos.

La segunda solución es mejor porque me permite traer mi atributo IOrderFilter antes que los atributos de terceros que no implementan IOrderFilter.

Muestra

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

Así que la ejecución sería

  • OrderableAttributeA
  • Atributo no determinable de la tercera parte
  • Atributo ordenableb
  • OrderableAttributeC

Así que aquí está el código modificado

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-01 09:36:35

Si tiene varios filtros del mismo tipo, la orden de ejecución será Global - > Controller - > Action

Y para el filtro de autorización, si establece varios filtros en diferentes niveles, se combinarán con un "Y" y se calcularán en el orden de ejecución anterior.

Y el proceso de autorización fallará en el primer filtro fallido.

Para más detalles, puede consultar esto post.

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-12 08:54:32