如何使用 DI 在 Class 构造函数中获取 Microsoft.AspNet.Http.HttpContext 实例
How to get Microsoft.AspNet.Http.HttpContext instance in Class Constructor using DI
我正在 MVC 6 中构建一次性应用程序并尝试不同的依赖架构。
我面临的问题是如何创建特定于应用程序的自定义“MyAppContext
”对象。这将需要来自 HttpContext
的一些信息和来自数据库的一些信息,并且将成为应用程序特定属性的请求范围的存储库。我想将 HttpContext
的实例传递给“MyAppContext
”的构造函数。
我已经使用 DI 成功创建了一个带有 IDataService
接口的“DataService
”对象,并且可以正常工作。
'MyAppContext' class 的不同之处在于它在构造函数中有两个参数 - 'DataService
' 和 Microsoft.AspNet.Http.HttpContext
。这是 MyAppContext class:
public class MyAppContext : IMyAppContext
{
public MyAppContext(IDataService dataService, HttpContext httpContext)
{
//do stuff here with the httpContext
}
}
在启动代码中,我注册了DataService实例和MyAppContext实例:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//adds a singleton instance of the DataService using DI
services.AddSingleton<IDataService, DataService>();
services.AddScoped<IMyAppContext, MyAppContext>();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseRequestServices();
app.UseMvc(routes => /* routes stuff */);
}
我期待构造函数中的 HttpContext
参数由 DI 解析。
当 运行 代码时,这是我返回的异常:
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNet.Http.HttpContext' while attempting to activate 'MyAppContext'
我想这是因为没有特定的 HttpContext
实例会发生此错误,但我不知道如何在 DI 中注册 HttpContext
实例。我添加了行 'app.UseRequestServices();
' 但这没有任何区别。我还尝试了以下变体:
services.AddScoped<HttpContext, HttpContext>();
但这失败了,因为第二个 HttpContext
应该是一个实例 - 我知道它不正确但无法弄清楚是什么。
所以,总而言之 - 如何将 HttpContext
对象传递到 MyAppContext 的构造函数中?
在构造函数中注入IHttpContextAccessor
为什么要在构造函数中传递 HttpContext?
为什么不在任何地方直接访问它?
public MyAppContext(IDataService dataService)
{
HttpContext mycontext = HttpContext.Current;
//do stuff here with mycontext
}
通过将 HttpContext
注入您的组件,您违反了 SOLID principles。更具体地说,您违反了:
- Dependency Inversion Principle(DIP)因为你依赖一个框架类型(
HttpContext
)。
- Interface Segregation Principle(ISP)因为
HttpContext
有很多方法,而消费者从来没有用过它们。
这两种违规行为都使测试您的代码变得更加困难。尽管您可以按照@victor 的建议注入 IHttpContextAccessor
,但这仍然违反了 DIP 和 ISP,因为这是框架提供的抽象,您仍然依赖于 HttpContext。根据 DIP,应该由客户来定义抽象。这会导致您的代码不必要地耦合到框架。
相反,您应该努力指定窄角色接口;为您做一件特定事情的接口,这些事情特定于您的应用程序的需要。注入一个带有字符串值的大字典(就像 HttpContext
一样,从来都不是很具体)。根据您的问题,我们不清楚您需要从我们的 MyAppContext
获得什么样的数据,但我希望得到诸如当前登录用户的信息之类的信息。为此,您可以定义特定的 IUserContext
抽象,例如:
public interface IUserContext {
IPrincipal CurrentUser { get; }
}
可以为此抽象轻松创建将应用程序连接到 ASP.NET 框架的适配器:
sealed class AspNetUserContextAdapter : IUserContext {
private readonly IHttpContextAccessor accessor;
public AspNetUserContextAdapter(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public IPrincipal CurrentUser => accessor.HttpContext.User;
}
此适配器确实依赖于 IHttpContextAccessor
,但这没关系,因为适配器是位于 Composition Root 中的基础结构组件。有几种方法可以注册这个 class,例如:
services.AddSingleton<IUserContext, AspNetUserContext>();
在启动中class:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvcCore();
}
在控制器中:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
private readonly IHttpContextAccessor _httpContextAccessor;
public ServerSentEventController(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
我正在 MVC 6 中构建一次性应用程序并尝试不同的依赖架构。
我面临的问题是如何创建特定于应用程序的自定义“MyAppContext
”对象。这将需要来自 HttpContext
的一些信息和来自数据库的一些信息,并且将成为应用程序特定属性的请求范围的存储库。我想将 HttpContext
的实例传递给“MyAppContext
”的构造函数。
我已经使用 DI 成功创建了一个带有 IDataService
接口的“DataService
”对象,并且可以正常工作。
'MyAppContext' class 的不同之处在于它在构造函数中有两个参数 - 'DataService
' 和 Microsoft.AspNet.Http.HttpContext
。这是 MyAppContext class:
public class MyAppContext : IMyAppContext
{
public MyAppContext(IDataService dataService, HttpContext httpContext)
{
//do stuff here with the httpContext
}
}
在启动代码中,我注册了DataService实例和MyAppContext实例:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//adds a singleton instance of the DataService using DI
services.AddSingleton<IDataService, DataService>();
services.AddScoped<IMyAppContext, MyAppContext>();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseRequestServices();
app.UseMvc(routes => /* routes stuff */);
}
我期待构造函数中的 HttpContext
参数由 DI 解析。
当 运行 代码时,这是我返回的异常:
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNet.Http.HttpContext' while attempting to activate 'MyAppContext'
我想这是因为没有特定的 HttpContext
实例会发生此错误,但我不知道如何在 DI 中注册 HttpContext
实例。我添加了行 'app.UseRequestServices();
' 但这没有任何区别。我还尝试了以下变体:
services.AddScoped<HttpContext, HttpContext>();
但这失败了,因为第二个 HttpContext
应该是一个实例 - 我知道它不正确但无法弄清楚是什么。
所以,总而言之 - 如何将 HttpContext
对象传递到 MyAppContext 的构造函数中?
在构造函数中注入IHttpContextAccessor
为什么要在构造函数中传递 HttpContext? 为什么不在任何地方直接访问它?
public MyAppContext(IDataService dataService)
{
HttpContext mycontext = HttpContext.Current;
//do stuff here with mycontext
}
通过将 HttpContext
注入您的组件,您违反了 SOLID principles。更具体地说,您违反了:
- Dependency Inversion Principle(DIP)因为你依赖一个框架类型(
HttpContext
)。 - Interface Segregation Principle(ISP)因为
HttpContext
有很多方法,而消费者从来没有用过它们。
这两种违规行为都使测试您的代码变得更加困难。尽管您可以按照@victor 的建议注入 IHttpContextAccessor
,但这仍然违反了 DIP 和 ISP,因为这是框架提供的抽象,您仍然依赖于 HttpContext。根据 DIP,应该由客户来定义抽象。这会导致您的代码不必要地耦合到框架。
相反,您应该努力指定窄角色接口;为您做一件特定事情的接口,这些事情特定于您的应用程序的需要。注入一个带有字符串值的大字典(就像 HttpContext
一样,从来都不是很具体)。根据您的问题,我们不清楚您需要从我们的 MyAppContext
获得什么样的数据,但我希望得到诸如当前登录用户的信息之类的信息。为此,您可以定义特定的 IUserContext
抽象,例如:
public interface IUserContext {
IPrincipal CurrentUser { get; }
}
可以为此抽象轻松创建将应用程序连接到 ASP.NET 框架的适配器:
sealed class AspNetUserContextAdapter : IUserContext {
private readonly IHttpContextAccessor accessor;
public AspNetUserContextAdapter(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public IPrincipal CurrentUser => accessor.HttpContext.User;
}
此适配器确实依赖于 IHttpContextAccessor
,但这没关系,因为适配器是位于 Composition Root 中的基础结构组件。有几种方法可以注册这个 class,例如:
services.AddSingleton<IUserContext, AspNetUserContext>();
在启动中class:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvcCore();
}
在控制器中:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
private readonly IHttpContextAccessor _httpContextAccessor;
public ServerSentEventController(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}