Asp.net 实现 IActionModelConvention 和 IFilterFactory 的核心自定义过滤器

Asp.net Core custom filter implementing IActionModelConvention and IFilterFactory

我需要创建一个实现 IActionModelConventionIFilterFactory 的自定义操作过滤器。

我使用IActionModelConvention同时设置多个路由,我使用IFilterFactory注入一些我需要使用的服务

问题IActionModelConvention 中的 Apply() 方法在 [=13= 中的 CreateInstance() 方法之前被调用],我需要注入的服务在 Apply().

中可用

我的问题 是如何在调用 Apply() 方法之前注入服务?而且我也更喜欢使用 IFilterFactory 来注入服务,因为它不会强制我用 [ServiceFilter][TypeFilter] 属性包装实际属性。

这是我的代码:

public class Contains2RoutesAttribute : Attribute, IActionModelConvention, IFilterFactory
{
    public ISomeService SomeService{ get; set; }

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        ISomeService someService = serviceProvider.GetService<ISomeService>();
        return new Contains2RoutesAttribute() { SomeService = someService };
    }

    public void Apply(ActionModel action)
    {
        // Here I need to use the service injected:
        this.SomeService.DoSomething(); // ERROR: The service here is null.

        action.Selectors.Clear();

        // Adding route 1:
        action.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel { Template = "~/index1" }
        });

        // Adding route 2:
        action.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel { Template = "~/index2" }
        });
    }
}

您的 IActionModelConvention 实施只会 运行 一次,在启动时。 Apply 将为每个操作调用一次。要在 Apply 函数内部使用 ISomeService,请将其作为构造函数参数传递。您的 Contains2RoutesAttribute class 不必是 IFilterFactory 的属性或实现,因为您已在评论中确认它不参与 filter pipeline。这是一个代码示例,其中我还重命名了 class 以更好地表示它在做什么(它不再是一个属性):

public class Contains2RoutesConvention : IActionModelConvention
{
    private readonly ISomeService someService;

    public Contains2RoutesConvention(ISomeService someService)
    {
        this.someService = someService;
    }

    public void Apply(ActionModel actionModel)
    {
        someService.DoSomething();

        ...
    }
}

Startup.ConfigureServices 中注册此约定:

services.AddMvc(options =>
{
    options.Conventions.Add(new Contains2RoutesConvention(new SomeService()));
});

这是它变得更有趣的地方。你不能使用 convention 的依赖注入,所以在这个例子中,我在构造 Contains2RoutesConvention 时内联创建了一个 SomeService 的实例。如果你希望这个实例是一个可以在你的应用程序的其他地方使用的单例,你可以在 ConfigureServices:

中做这样的事情
var someService = new SomeService();

services.AddMvc(options =>
{
    options.Conventions.Add(new Contains2RoutesConvention(someService));
});

services.AddSingleton<ISomeService>(someService);

当然,这取决于 SomeService 是否有自己的依赖项,但如果有,它们将无法从 DI 容器中解析,因为它在管道中还为时过早。