Startup.Configure

时间:2017-04-14 11:18:56

标签: asp.net dependency-injection configuration asp.net-core startup

我正在使用Cookie中间件来验证用户身份。我一直关注this official tutorial

在我的Startup课程中,我Configure方法的摘录如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
  // ...

  // Cookie-based Authentication
  app.UseCookieAuthentication(new CookieAuthenticationOptions()
  {
    AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,        
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    Events = new CustomCookieAuthenticationEvents(app),
  });

  // ...
}

CustomCookieAuthenticationEvents类的定义如下:

public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
  private IApplicationBuilder _app;
  private IMyService _myService = null;
  private IMyService MyService
  {
    get
    {
      if(_myService != null)
      {
        return _myService;
      } else
      {
        return _myService = (IMyService) _app.ApplicationServices.GetService(typeof(IMyService));
      }
    }
  }

  public CustomCookieAuthenticationEvents(IApplicationBuilder app)
  {
    _app = app;
  }

  public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
  {
    string sessionToken = context.Principal.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid)?.Value;
    LogonSession response = null;

    var response = await MyService.CheckSession(sessionToken);

    if (response == null)
    {
      context.RejectPrincipal();
      await context.HttpContext.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    }
  }
}

由于Startup.Configure无法使用依赖注入(此时服务甚至没有注册),我做了一些解决方法:

  1. 将IApplicationBuilder服务传递给CustomCookieAuthenticationEvents
  2. 在只读属性(单例模式)中第一次请求时获取IMyService
  3. TL;博士

    我的解决方案有效,但丑陋。没有涉及依赖注入,因为那时不可能。

    问题的实质是我必须实例化CustomCookieAuthenticationEvents。至于我已经阅读了source code,没有办法解决这个问题,因为如果省略UseCookieAuthentication参数,options会引发异常。

    有任何建议如何使我当前的解决方案更好

2 个答案:

答案 0 :(得分:7)

在Startup.Configure()之前调用Startup.ConfigureServices()(有关更多信息,请参阅https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup)。所以当时可以使用依赖注入;)
因此,您可以在配置方法中解决您的依赖关系,如下所示:

app.ApplicationServices.GetRequiredService<CustomCookieAuthenticationEvents>()

答案 1 :(得分:3)

解决中间件内的服务时应该非常小心。当您使用/ need / require范围服务(即使用DbContext)时,您当前的方法(以及@arnaudauroux建议的方法)可能会导致困难。

当服务注册为app.ApplicationServices时,通过scoped解析会导致静态(单一)服务(每次调用都会解决瞬态,因此它们不受影响)。最好在HttpContext ValidatePrincipal方法内的public override async Task ValidatePrincipal(CookieValidatePrincipalContext context) { string sessionToken = context.Principal.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid)?.Value; LogonSession response = null; var myService = context.HttpContext.RequestServices.GetService<IMyService >(); var response = await myService.CheckSession(sessionToken); if (response == null) { context.RejectPrincipal(); await context.HttpContext.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); } } 请求期间解析您的服务。

CustomCookieAuthenticationEvents

使用这种方法,您根本不需要在HttpContext.RequiredServices类中传递任何依赖项。 app.ApplicationServices专门用于这样的类(任何其他类都可以通过构造函数注入来解决,但不能解决中间件和http上下文相关的管道,因为没有其他方法可以正确解决中间件中的作用域服务 - 中间件实例是静态的,只有每个请求实例化一次)

这样您就不会遇到使用范围服务的终生问题。 解决临时服务时,它们将在请求结束时处理。通过{{1}}解决的临时服务将在请求完成后以及垃圾收集触发时在某个时间点解决(意味着:您的资源将在尽可能早的时候释放,即请求结束时)。