【问题标题】:How to get Microsoft.AspNet.Http.HttpContext instance in Class Constructor using DI如何使用 DI 在类构造函数中获取 Microsoft.AspNet.Http.HttpContext 实例
【发布时间】:2015-07-15 07:44:35
【问题描述】:

我正在 MVC 6 中构建一个一次性应用程序,并尝试使用不同的依赖关系架构。

我面临的问题是如何创建特定于应用程序的自定义“MyAppContext”对象。这将需要来自HttpContext 的一些信息和来自数据库的一些信息,并且将是应用程序特定属性的请求范围的存储库。我想将HttpContext 的实例传递给'MyAppContext'的构造函数。

我已经使用 DI 成功地创建了一个带有 IDataService 接口的“DataService”对象,这可以正常工作。 与“MyAppContext”类的不同之处在于它在构造函数中有两个参数——“DataService”和Microsoft.AspNet.Http.HttpContext。这是 MyAppContext 类:

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:尝试激活“MyAppContext”时无法解析“Microsoft.AspNet.Http.HttpContext”类型的服务

我认为这是因为没有 HttpContext 的特定实例导致此错误发生,但我不知道如何在 DI 中注册 HttpContext 实例。我添加了“app.UseRequestServices();”行,但这并没有什么不同。我还尝试了以下变体:

services.AddScoped<HttpContext, HttpContext>();

但这失败了,因为第二个 HttpContext 应该是一个实例 - 我知道它不正确但无法弄清楚是什么。

所以,总而言之 - 我如何将 HttpContext 对象传递到 MyAppContext 的构造函数中?

【问题讨论】:

标签: c# asp.net-mvc dependency-injection asp.net-core asp.net-core-mvc


【解决方案1】:

在构造函数中注入IHttpContextAccessor

【讨论】:

  • 是的,这行得通。特别是它的 Microsoft.AspNet.Hosting.IHttpContextAccessor 类型,然后提供包含 HttpContext 的 .Value 属性。
【解决方案2】:

通过将HttpContext 注入到您的组件中,您违反了SOLID principles。更具体地说,您违反了:

这两种违规行为都使测试代码变得更加困难。尽管您可以像@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 中的基础设施组件。有几种注册此类的方法,例如:

services.AddSingleton<IUserContext, AspNetUserContext>();

【讨论】:

  • 我本能地知道直接绑定到 HttpContext 是不正确的,我期待找到一个接口来使用(这就是 IHttpContextAccessor 是什么)。该应用程序没有任何目的,我只是通过实际示例来探索这些问题。我理解显示的示例 - 我对这种类型的唯一担忧是,如果不使用 IHttpContextAccessor(或类似),每次发现新需求时,您都必须继续返回并重新定义构造函数参数。 (首先是用户,然后是查询字符串,然后是请求标头等)。
  • @BruceChapman:如果您发现自己不得不返回“并在每次发现新需求时重新定义构造函数参数”,这可能表明违反了单一责任原则。 IUserContext 抽象除了获取当前用户外,不应该做任何其他事情。如果您需要来自查询字符串等的数据,您可能会得到不同的信息,这值得单独抽象。困难的部分当然是正确地进行抽象,但如果您从坚持基本原则(SOLID 原则)开始,通常会容易得多。
  • HttpContext.Current 在 vNext 中消失了
  • HttpContext 是抽象类,不是具体类型。
  • @DaveVandenEynde:Nope。该类甚至标有sealed 关键字。
【解决方案3】:

在启动类中:

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;
}

【讨论】:

    【解决方案4】:

    为什么要在构造函数中传递 HttpContext? 为什么不直接在任何你想要的地方访问它?

    public MyAppContext(IDataService dataService)
        {
           HttpContext mycontext = HttpContext.Current;
           //do stuff here with mycontext
        }
    

    【讨论】:

    • 这是我试图通过使用 DI 来避免的那种类依赖。这种类型的代码在类中嵌入了一个依赖项,并且不清楚 HttpContext 引用的来源。
    • 这在 ASP.NET 5 中不起作用。在 MVC 5/4/3 应用程序中也是一个非常糟糕的主意。
    • HttpContext.Current 已在 vNext 中删除
    猜你喜欢
    • 2020-02-14
    • 2010-11-19
    • 2017-01-05
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多