seguridad por ejemplo basada autenticacion c# asp.net asp.net-web-api action-filter asp.net-web-api2

c# - por - Orden de ejecución con múltiples filtros en la API web



web api rest c# (2)

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 puedo definir el orden de ejecución en la aplicación 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 para mí?


Algunas cosas a tener en cuenta aquí:

  1. Los filtros se ejecutan en el siguiente orden para una acción: filtros definidos globalmente -> filtros específicos del controlador -> filtros específicos de acción.
  2. Filtros de autorización -> Filtros de acción -> Filtros de excepción
  3. Ahora el problema que pareces mencionar está relacionado con tener filtros múltiples del mismo tipo (por ejemplo, Multiple ActionFilterAttribute decorado en un controlador o una acción. Este es el caso que no garantizaría el orden ya que está basado en la reflexión). Para este caso, hay una manera de hacerlo en la API web mediante 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; } }


Tuve algunos problemas con la solución de la respuesta de Kiran Challa. 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 IOrderedFilter filtros que implementen IOrderedFilter . Tenía un atributo de un tercero que se corta y como resultado no se ejecuta.

Entonces tuve dos posibles soluciones.

  1. Use herencia para crear una versión extendida del atributo de terceros para que también implemente IOrderFilter .
  2. Cambie el método para tratar cada atributo que no implemente IOrderFilter como un atributo IOrderFilter con el número de orden 0 y combínelo con los atributos IOrderFilter , ordene y devuélvalos.

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

Muestra

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

Entonces la ejecución sería

  • OrderableAttributeA
  • NonOrderableThirdPartyAttribute
  • OrderableAttributeB
  • 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); } }