ASP.NET 内部的核心依赖注入 Startup.Configure
ASP.NET Core Dependency Injection inside Startup.Configure
我正在使用 Cookie 中间件对用户进行身份验证。我一直在关注 this official tutorial.
在我的 Startup
class 中,我的 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
class定义如下:
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
不可用(那时甚至还没有注册服务),我做了一些解决方法:
- 将 IApplicationBuilder 服务传递给
CustomCookieAuthenticationEvents
class
- 在只读 属性(单例模式)中首次请求时获取
IMyService
tl;博士
我的解决方案可行,但丑陋。不涉及依赖注入,因为当时不可能。
问题的实质是我必须实例化CustomCookieAuthenticationEvents
。据我阅读 source code,没有办法解决这个问题,因为如果我省略 options
参数,UseCookieAuthentication
会抛出异常。
任何建议如何使我当前的解决方案更好?
Startup.ConfigureServices() 在 Startup.Configure() 之前调用(有关详细信息,请参阅 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup)。所以依赖注入在那个时候是可用的 ;)
因此,您可以像这样解决您对配置方法的依赖:
app.ApplicationServices.GetRequiredService<CustomCookieAuthenticationEvents>()
在中间件内部解析服务时,您应该非常小心。当您 use/need/require 作用域服务(即 DbContext 的使用)时,您当前的方法(以及 @arnaudaroux 建议的方法)可能会导致困难。
通过 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
class 中传递任何依赖项。 HttpContext.RequiredServices
是专门为这样的 classes 制作的(任何其他的都可以通过构造函数注入来解决,但不能通过中间件和与 http 上下文相关的管道解决,因为没有其他方法可以正确解析中间件中的作用域服务 - Middleware实例是静态的,每个请求只实例化一次)
这样您的作用域服务就不会出现生命周期问题。
当您解析瞬态服务时,它们将在请求结束时被处理掉。而通过 app.ApplicationServices
解析的瞬时服务将在请求完成和垃圾收集触发后的某个时间点解析(意味着:您的资源将尽早释放,即请求结束时)。
我正在使用 Cookie 中间件对用户进行身份验证。我一直在关注 this official tutorial.
在我的 Startup
class 中,我的 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
class定义如下:
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
不可用(那时甚至还没有注册服务),我做了一些解决方法:
- 将 IApplicationBuilder 服务传递给
CustomCookieAuthenticationEvents
class - 在只读 属性(单例模式)中首次请求时获取
IMyService
tl;博士
我的解决方案可行,但丑陋。不涉及依赖注入,因为当时不可能。
问题的实质是我必须实例化CustomCookieAuthenticationEvents
。据我阅读 source code,没有办法解决这个问题,因为如果我省略 options
参数,UseCookieAuthentication
会抛出异常。
任何建议如何使我当前的解决方案更好?
Startup.ConfigureServices() 在 Startup.Configure() 之前调用(有关详细信息,请参阅 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup)。所以依赖注入在那个时候是可用的 ;)
因此,您可以像这样解决您对配置方法的依赖:
app.ApplicationServices.GetRequiredService<CustomCookieAuthenticationEvents>()
在中间件内部解析服务时,您应该非常小心。当您 use/need/require 作用域服务(即 DbContext 的使用)时,您当前的方法(以及 @arnaudaroux 建议的方法)可能会导致困难。
通过 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
class 中传递任何依赖项。 HttpContext.RequiredServices
是专门为这样的 classes 制作的(任何其他的都可以通过构造函数注入来解决,但不能通过中间件和与 http 上下文相关的管道解决,因为没有其他方法可以正确解析中间件中的作用域服务 - Middleware实例是静态的,每个请求只实例化一次)
这样您的作用域服务就不会出现生命周期问题。
当您解析瞬态服务时,它们将在请求结束时被处理掉。而通过 app.ApplicationServices
解析的瞬时服务将在请求完成和垃圾收集触发后的某个时间点解析(意味着:您的资源将尽早释放,即请求结束时)。