【问题标题】:Scope issues when using Ninject BindHttpFilter使用 Ninject BindHttpFilter 时的范围问题
【发布时间】:2014-10-29 18:27:50
【问题描述】:

我有一个 WebApi 服务,我正在尝试使用 Ninject BindHttpFilter 添加身份验证。

使用 BindHttpFilter 可以将身份验证过滤器绑定到特定属性。 AuthenticationFilter 接受一个构造函数参数(IAuthenticationService),它本身是由 Ninject 创建的。

kernel.BindHttpFilter<AuthenticationHttpFilter>(System.Web.Http.Filters.FilterScope.Action)
    .WhenActionMethodHas<AuthenticationFilterAttribute>()
    .WithConstructorArgument("service", x => x.Kernel.Get<IAuthenticationService>());

AuthenticationService 的具体实现采用了一个构造函数参数 INonceRepository,通过 Ninject 注入:

public AuthenticationService(INonceRepository nonceRepository, ...)

NonceRepository 的具体实现采用了一个通过 Ninject 注入的构造函数 ISession:

public NonceRepository(ISession session)

Ninject 绑定如下所示:

kernel.Bind<INonceRepository>().To<NonceRepository>();
kernel.Bind<IAuthenticationService>().To<AuthenticationService>()

var session = sessionFactory.OpenSession();

Bind<ISession>().ToMethod(c => session).InRequestScope();

当代码运行时,AuthenticationService 的具体实现只被实例化一次,因此 NonceRepositiory 只被实例化一次。这意味着 ISession 有效并打开第一个请求,但 ISession 在第二次调用时关闭,并且第二次调用 AuthenticationService 的构造函数。这似乎是一个范围问题,但我无法弄清楚什么没有正确的范围来使 AuthenticationService 根据请求重新创建。

我尝试将 BindHttpScope 请求从 FilterScope.Controller 更改为 FilterScope.Action(认为这会导致 AuthenticationService 的范围是它是每个 Action 调用创建的),但这并没有解决问题。

下面是代码的有趣点:

public class AuthenticationHttpFilter : IAuthenticationFilter
{
    private readonly IAuthenticationService authenticationService;

    public AuthenticationHttpFilter(IAuthenticationService service)
    {
        this.authenticationService = service;
    }

    public bool AllowMultiple { get; private set; }

    public Task AuthenticateAsync(HttpAuthenticationContext authenticationContext, CancellationToken cancellationToken)
    {
        authenticationService.DoAuth();
        return Task.FromResult(0);
    }

    public Task ChallengeAsync(HttpAuthenticationChallengeContext authenticationChallengeContext, CancellationToken cancellationToken)
    {
    ...
    }
}

public class AuthenticationService : IAuthenticationService
{
    private readonly INonceRepository nonceRepo;

    public AuthenticationService(INonceRepository nonceRepo){...}

    public void DoAuth()
    {
        this.nonceRepo.Add(...);
    }
}

public class NonceRepository : INonceRepository
{
    private readonly ISession _session;

    public NonceRepository(ISession session)
    {
        this._session = session;
    }

    public void Add(Nonce nonce)
    {
        this._session.Save(nonce);
    }
}

【问题讨论】:

  • 肯特,你解决过这个问题吗?我有一个非常相似的问题。
  • Jason,我回来了一点,但是......我相信问题是: var session = sessionFactory.OpenSession(); Bind().ToMethod(c => session).InRequestScope();会话是在请求中创建的,但按请求使用。解决方案是将会话的打开移动到每个请求调用中: Bind().ToMethod( c => sessionFactory.OpenSession()).InRequestScope();我与之抗争的另一件事是确保您始终在使用 WebApi 与 MVC 时保持同步。很多时候名称是相同的,只是名称空间不同。
  • 感谢您的回复。我的问题最终是 WebAPI 缓存了过滤器,所以我的构造函数依赖项只注入了一次。

标签: ninject asp.net-web-api2


【解决方案1】:

我发现当我使用 WebApi 2 时,一切正常。它在 RequestScope 中的行为与我预期的一样。但是,在使用 MVC 5 时,过滤器会一直缓存在管道中,这会将构造函数参数保持在“Singleton”范围内。

我通过像这样进入actionContext 的配置解决了这个问题:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class EventGridFilterAttribute : ActionFilterAttribute
{
    private readonly Type serviceType = typeof(IEventGridService);

    public EventGridFilterAttribute()
    {
    }

    public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
    {
        var service = actionExecutedContext?.ActionContext?.ControllerContext?.Configuration?.DependencyResolver?.GetService(serviceType) as IEventGridService;

        if (service != null && actionExecutedContext.Exception == null)
        {
            await service.PublishEventsAsync();
        }

        await base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
    }
}

我的依赖注入器保存在这里。

GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);

我也不使用BindHttpFilter,而是像普通过滤器一样注册我的过滤器。

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new EventGridFilterAttribute());
}

【讨论】:

    猜你喜欢
    • 2023-03-19
    • 1970-01-01
    • 2018-12-01
    • 1970-01-01
    • 2018-09-01
    • 2021-07-17
    • 2020-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多