Я создал IFilterProvider для своего приложения asp.net mvc core 2.0. Я пытаюсь получить ту же функциональность, что и Поставщик условных фильтров Фила Хаака
Я зарегистрировал поставщика фильтров в коллекции сервисов как одноэлементный, но мои атрибуты не выполняются. Я вижу, как выполняется метод OnProvidersExecuting, если я отлаживаю, но это все. Атрибуты работают правильно, если я статически добавляю их к действиям контроллера.
Атрибуты, которые я добавляю, - это TypeFilterAttributes, не уверен, что это имеет значение.
Буду признателен за любой совет, который вы дадите!
Вот код:
public class PermissionFilterProvider : IFilterProvider
{
private readonly Func<ActionContext, IFilterMetadata>[] conditions;
public PermissionFilterProvider()
{
conditions = new Func<ActionContext, IFilterMetadata>[]
{
(c) => c.HttpContext.Request.Method == HttpMethod.Get.Method
? new PermissionAttribute(Permission.Read)
: null,
(c) => c.HttpContext.Request.Method == HttpMethod.Post.Method
? new PermissionAttribute(Permission.Create)
: null,
(c) => c.HttpContext.Request.Method == HttpMethod.Put.Method
? new PermissionAttribute(Permission.Write)
: null,
(c) => c.HttpContext.Request.Method == HttpMethod.Delete.Method
? new PermissionAttribute(Permission.Delete)
: null
};
}
public int Order => 0;
public void OnProvidersExecuted(FilterProviderContext context)
{
}
public void OnProvidersExecuting(FilterProviderContext context)
{
var filterMetas = conditions.Select(c => c(context.ActionContext)).OfType<IFilterMetadata>().First();
Debug.WriteLine($"Adding Filter Metadata {filterMetas.GetType()} for action {context.ActionContext.ActionDescriptor.DisplayName}.");
context.Results.Add(
new FilterItem(
new FilterDescriptor(filterMetas, FilterScope.Global),
filterMetas
)
);
}
}
Обновлять:
Ответ Сета поставил меня на путь.
Мой класс PermissionAttribute был излишне сложным. Атрибут теперь представляет собой простой маркер, и у меня есть глобальный класс PermissionFilter, который выполняет работу, если атрибут присутствует. Со всем этим работает поставщик фильтров.
Вот код атрибута и глобального фильтра:
public class PermissionAttribute : Attribute, IFilterMetadata
{
public PermissionAttribute(Permission Permissions)
{
this.Permissions = Permissions;
}
public Permission Permissions { get; private set; }
}
public class PermissionFilter : IAsyncResourceFilter
{
private readonly IAuthorizationService authService;
public PermissionFilter(IAuthorizationService authService)
{
this.authService = authService;
}
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
var action = context.ActionDescriptor as ControllerActionDescriptor;
var attribute = context.Filters.OfType<PermissionAttribute>().SingleOrDefault();
if (attribute != null)
{
var requirement = new PermissionsAuthorizationRequirement(attribute.Permissions);
...
}
}
}





Пытаясь воспроизвести вашу проблему, я создал следующий фильтр, и ваш код у меня работает (вызывается OnActionExecuted, OnActionExecuting):
public enum Permission
{
Read, Create, Write, Delete
}
public class PermissionAttribute : TypeFilterAttribute, IActionFilter
{
public PermissionAttribute(Permission p) : this(typeof(PermissionAttribute))
{}
public PermissionAttribute(Type type) : base(type)
{}
public void OnActionExecuted(ActionExecutedContext context)
{
// this is called
}
public void OnActionExecuting(ActionExecutingContext context)
{
// this is called
}
}
Если это не то, чего вы хотите достичь, отредактируйте свой вопрос, указав свою реализацию PermissionAttribute.
Но если я понимаю вашу идею, то дело в том, что вам вообще не нужно использовать TypeFilterAttribute, а вместо этого просто определить нужный тип фильтра, например
public class PermissionAttribute : IActionFilter
{
public PermissionAttribute(IMyDependency dependency)
{ ... }
// IActionFilter interface implementation
}
поскольку основная цель TypeFilterAttribute - помочь разрешить необходимые зависимости от контейнера DI. И поскольку вы используете FilterProvider для создания и присоединения фильтров к действиям, вы можете напрямую внедрить необходимые зависимости в FilterProvider через конструктор и передать их при создании экземпляра фильтра:
public PermissionFilterProvider(IMyDependency dependency)
{
(c) => c.HttpContext.Request.Method == HttpMethod.Get.Method
? new PermissionAttribute(dependency)
: null,
...
}
// Note: IMyDependency implementation should be registered to DI container