Фильтры Autofa c ApiController для методов с атрибутом - PullRequest
1 голос
/ 02 апреля 2020

Цель состоит в том, чтобы преобразовать несколько Attr в DI-фильтр из Autofa c, используя .AsWebApiAuthorizationFilterFor<ApiController>(c => c.GetType()

Чувствую, что я близко, но, вероятно, просто делаю это неправильно, нашел кучу сообщений о инжекторе peram но это не то, что я хочу сделать здесь.

текущая неудачная попытка

       builder.RegisterType<SomeFilter>()
            .AsWebApiAuthorizationFilterFor<ApiController>(c => c.GetType()
                .GetMethods()
                .Where(m => m.GetCustomAttributes<SomeAttribute>().Any())
            )
            .InstancePerRequest();

Цель: добавить этот фильтр для всех контроллеров API и / или методов контроллера с указанным атрибутом c

Решение создано Из предоставленных ответов:

  1. Создайте пользовательский IAutofacAuthorizationFilter и переместите текущие логи фильтра c в ATTR в метод аутентификации этого интерфейса.
  2. Добавлено разрешение вызова фильтр для автофа c builder.RegisterWebApiFilterProvider(config);
  3. Registered My Filter

        builder.RegisterType<MyFilterFilter>()
            .AsWebApiAuthorizationFilterFor<BaseTypeApiController>()
            .InstancePerDependency();
    
  4. Добавить проверку исходного атрибута на контроллере или методе, если оба возвращаются, когда я возвращаюсь.

        var controllerAttribute = actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AttributeReplacedByFilterAttribute>().FirstOrDefault();
        var methodAttribute = actionContext.ActionDescriptor.GetCustomAttributes<AttributeReplacedByFilterAttribute>().FirstOrDefault();
    

Пока что кажется, что все получается и решается для моих требований.

Ответы [ 2 ]

2 голосов
/ 02 апреля 2020

Второй параметр AsWebApiAuthorizationFilterFor - это селектор действий, используемый для выбора одного метода, для которого создается фильтр.

Чтобы применить условную регистрацию, вы можете использовать OnlyIf:

// Conditionally register filter for all controller actions
services.GetContainerBuilder()
    .RegisterType<Filter>()
    .AsWebApiAuthorizationFilterFor<DummyController>()
    .OnlyIf(_ => typeof(DummyController).GetCustomAttributes<SomeAttribute>().Any());

// Conditionally register filter for selected controller action
services.GetContainerBuilder()
    .RegisterType<Filter>()
    // x => x.Get selects Get method defined by DummyController
    .AsWebApiAuthorizationFilterFor<DummyController>(x => x.Get())
    .OnlyIf(_ => 
     typeof(DummyController).GetMethod("Get").GetCustomAttributes<SomeAttribute>().Any());

Вы также можете создать собственное расширение AsWebApiAuthorizationFilterForOnlyIf и сделать:

if (typeof(DummyController).GetCustomAttributes<SomeAttribute>().Any())
{
    services.GetContainerBuilder()
        .RegisterType<Filter>()
        .AsWebApiAuthorizationFilterFor<DummyController>();
}

ОБНОВЛЕНИЕ

Если вам нравится scan-and-add-automatically метод, вы можете использовать дерево выражений для динамического вызова AsWebApiAuthorizationFilterFor для каждого найденного контроллера и метода. Там будет довольно много кода, как вы могли видеть ниже:

ContainerBuilder containerBuilder = ...
Type[] controllerTypes = ...

// Keep the type registration builder here, and use it for all As actions
var typeRegistrationBuilder = containerBuilder
    .RegisterType<Filter>()
    .InstancePerRequest();

// Scan controllers and methods and build registrations
foreach (Type controllerType in controllerTypes)
{
    // Which means ALL actions in the controller should have the filter registered
    if (controllerType.GetCustomAttributes<SomeAttribute>().Any())
    {
        // Because AsWebApiAuthorizationFilterFor doesn't take non-generic type parameter,
        // so we need to build delegate to invoke AsWebApiAuthorizationFilterFor<controllerType>.

        // Use expression tree.

        // This will build:
        // () => typeRegistrationBuilder.AsWebApiAuthorizationFilterFor<controllerType>()
        Expression.Lambda<Action>(
            Expression.Call(
                typeof(Autofac.Integration.WebApi.RegistrationExtensions),
                "AsWebApiAuthorizationFilterFor",
                new[] { controllerType },
                Expression.Constant(typeRegistrationBuilder))
        ).Compile()(); // Just compile and run this Action

        continue;
    }

    // Controller doesn't have the attribute
    // Scan action methods

    var methods = controllerType.GetMethods()
        .Where(mi => mi.GetCustomAttributes<SomeAttribute>().Any());
    foreach (MethodInfo method in methods)
    {
        // Now the method has the attribute
        // Again, use expression tree to build method call, but this time, with a selector

        // First, build method call expression parameters for each parameter on the method
        // Just need to pass default(T) in, because Autofac literally just need the method
        IEnumerable<Expression> parameters = method.GetParameters()
            .Select(p => p.ParameterType)
            .Select(Expression.Default);

        // Build method selector
        // This will build:
        // _ => _.SomeMethod(parameters)

        ParameterExpression controller = Expression.Parameter(controllerType, "_");
        LambdaExpression methodSelector = Expression.Lambda(
            typeof(Action<>).MakeGenericType(controllerType),
            Expression.Call(controller, method, parameters),
            controller);

        // This will build:
        // () => typeRegistrationBuilder.AsWebApiAuthorizationFilterFor<controllerType>(
        //     _ => _.SomeMethod(parameters));
        Expression.Lambda<Action>(
            Expression.Call(
                typeof(Autofac.Integration.WebApi.RegistrationExtensions),
                "AsWebApiAuthorizationFilterFor",
                new[] { controllerType },
                Expression.Constant(typeRegistrationBuilder),
                methodSelector)
        ).Compile()(); // Just compile and run this Action
    }
}
0 голосов
/ 03 апреля 2020

Решение создано из предоставленных ответов:

  1. Создайте пользовательский IAutofacAuthorizationFilter и переместите текущие логи фильтра c в ATTR в метод аутентификации этого интерфейса.
  2. Добавлен фильтр разрешения вызовов для автофона c builder.RegisterWebApiFilterProvider(config);
  3. Зарегистрированный мой фильтр

        builder.RegisterType<MyFilterFilter>()
            .AsWebApiAuthorizationFilterFor<BaseTypeApiController>()
            .InstancePerDependency();
    
  4. Добавить проверку исходного атрибута на контроллере или методе, если оба равны нулю, я возвращаюсь. Добавлена ​​проверка для метода фильтрации: OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)

        var controllerAttribute = actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AttributeReplacedByFilterAttribute>().FirstOrDefault();
        var methodAttribute = actionContext.ActionDescriptor.GetCustomAttributes<AttributeReplacedByFilterAttribute>().FirstOrDefault();
    

Пока что, кажется, все работает и решается для моих требований.

Примечание: вы можете использовать один и тот же метод, чтобы применить фильтр ко всем методам контроллера, а затем проверить атрибут. для моего варианта использования у меня был базовый класс для контроллеров, на которые мне нужно было воздействовать. Для регистрации всех измените регистрацию фильтра на .AsWebApiAuthorizationFilterFor<ApiController>()

...