Как получить HttpContext.Current в ASP.NET Core?

В настоящее время мы переписываем/конвертируем наше приложение ASP.NET WebForms с использованием ASP.NET Core. Попытка избежать реинжиниринга как можно больше.

Существует раздел, в котором мы используем HttpContext в библиотеке классов для проверки текущего состояния. Как я могу получить доступ к HttpContext.Current в .NET Core 1.0?

 var current = HttpContext.Current;
     if (current == null)
      {
       // do something here
       // string connection = Configuration.GetConnectionString("MyDb");
      }

Мне нужно получить доступ к этому, чтобы создать текущий хост приложения.

$"{current.Request.Url.Scheme}://{current.Request.Url.Host}{(current.Request.Url.Port == 80 ? "" : ":" + current.Request.Url.Port)}";

Ответ 1

Как правило, преобразование приложения Web Forms или MVC5 в ASP.NET Core потребует значительного объема рефакторинга.

HttpContext.Current был удален в ASP.NET Core. Доступ к текущему HTTP-контексту из отдельной библиотеки классов - это тип грязной архитектуры, которую ASP.NET Core пытается избежать. Есть несколько способов перестроить это в ASP.NET Core.

Свойство HttpContext

Вы можете получить доступ к текущему HTTP-контексту через свойство HttpContext на любом контроллере. Самым близким к вашему исходному HttpContext кода будет передать HttpContext в метод, который вы вызываете:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        MyMethod(HttpContext);

        // Other code
    }
}

public void MyMethod(Microsoft.AspNetCore.Http.HttpContext context)
{
    var host = $"{context.Request.Scheme}://{context.Request.Host}";

    // Other code
}

Параметр HttpContext в промежуточном программном обеспечении

Если вы пишете пользовательское промежуточное программное обеспечение для конвейера ASP.NET Core, текущий запрос HttpContext передается в ваш метод Invoke:

public Task Invoke(HttpContext context)
{
    // Do something with the current HTTP context...
}

Средство доступа к контексту HTTP

Наконец, вы можете использовать вспомогательную службу IHttpContextAccessor для получения HTTP-контекста в любом классе, который управляется системой внедрения зависимостей ASP.NET Core. Это полезно, когда у вас есть общая служба, используемая вашими контроллерами.

Запросите этот интерфейс в вашем конструкторе:

public MyMiddleware(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}

Затем вы можете получить доступ к текущему HTTP-контексту безопасным способом:

var context = _httpContextAccessor.HttpContext;
// Do something with the current HTTP context...

IHttpContextAccessor не всегда добавляется в контейнер службы по умолчанию, поэтому зарегистрируйте его в ConfigureServices просто для безопасности:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();
    // if < .NET Core 2.2 use this
    //services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Other code...
}

Ответ 2

Necromancing.

ДА ВЫ МОЖЕТЕ, и вот как.

Тайный совет для тех, кто переносит большие куски junks:
Следующий метод - злой карбункул взлома, который активно участвует в выполнении экспресс-работы сатаны (в глазах разработчиков платформы .NET Core), , но работает:

В public class Startup

добавить свойство

public IConfigurationRoot Configuration { get; }

И затем добавьте singleton IHttpContextAccessor в DI в ConfigureServices.

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();

Затем в Configure

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

добавьте параметр DI IServiceProvider svp, поэтому метод выглядит следующим образом:

    public void Configure(
           IApplicationBuilder app
          ,IHostingEnvironment env
          ,ILoggerFactory loggerFactory
          ,IServiceProvider svp)
    {

Затем создайте класс замены для System.Web:

namespace System.Web
{

    namespace Hosting
    {
        public static class HostingEnvironment 
        {
            public static bool m_IsHosted;

            static HostingEnvironment()
            {
                m_IsHosted = false;
            }

            public static bool IsHosted
            {
                get
                {
                    return m_IsHosted;
                }
            }
        }
    }


    public static class HttpContext
    {
        public static IServiceProvider ServiceProvider;

        static HttpContext()
        { }


        public static Microsoft.AspNetCore.Http.HttpContext Current
        {
            get
            {
                // var factory2 = ServiceProvider.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
                object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));

                // Microsoft.AspNetCore.Http.HttpContextAccessor fac =(Microsoft.AspNetCore.Http.HttpContextAccessor)factory;
                Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
                // context.Response.WriteAsync("Test");

                return context;
            }
        }


    } // End Class HttpContext 


}

Теперь в Configure, где вы добавили IServiceProvider svp, сохраните этот поставщик услуг в статической переменной "ServiceProvider" только что созданного фиктивного класса System.Web.HttpContext(System.Web.HttpContext.ServiceProvider)

и установите HostingEnvironment.IsHosted в true

System.Web.Hosting.HostingEnvironment.m_IsHosted = true;

это по сути то, что сделал System.Web, только что вы его никогда не видели (я думаю, переменная была объявлена ​​как внутренняя, а не публичная).

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    ServiceProvider = svp;
    System.Web.HttpContext.ServiceProvider = svp;
    System.Web.Hosting.HostingEnvironment.m_IsHosted = true;


    app.UseCookieAuthentication(new CookieAuthenticationOptions()
    {
        AuthenticationScheme = "MyCookieMiddlewareInstance",
        LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Unauthorized/"),
        AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Forbidden/"),
        AutomaticAuthenticate = true,
        AutomaticChallenge = true,
        CookieSecure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest

       , CookieHttpOnly=false

    });

Как и в ASP.NET Web-Forms, вы получите NullReference, когда вы пытаетесь получить доступ к HttpContext, когда его нет, например, он использовался в Application_Start в global.asax.

Я снова подчеркиваю: это работает только если вы действительно добавили

services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();

как я написал, вы должны.

Добро пожаловать в шаблон ServiceLocator в шаблоне DI;)
Для рисков и побочных эффектов обратитесь к своему резидентному врачу или фармацевту или изучите источники .NET Core на github.com/aspnet и выполните некоторые тесты.


Возможно, более поддерживаемый метод будет добавлять этот вспомогательный класс

namespace System.Web
{

    public static class HttpContext
    {
        private static Microsoft.AspNetCore.Http.IHttpContextAccessor m_httpContextAccessor;


        public static void Configure(Microsoft.AspNetCore.Http.IHttpContextAccessor httpContextAccessor)
        {
            m_httpContextAccessor = httpContextAccessor;
        }


        public static Microsoft.AspNetCore.Http.HttpContext Current
        {
            get
            {
                return m_httpContextAccessor.HttpContext;
            }
        }


    }


}

И затем вызовите HttpContext.Configure в Startup- > Configure

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();


    System.Web.HttpContext.Configure(app.ApplicationServices.
        GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>()
    );

Ответ 3

Это решение, если вам действительно нужен статический доступ к текущему контексту. В Startup.Configure(....)

app.Use(async (httpContext, next) =>
{
    CallContext.LogicalSetData("CurrentContextKey", httpContext);
    try
    {
        await next();
    }
    finally
    {
        CallContext.FreeNamedDataSlot("CurrentContextKey");
    }
});

И когда вам это нужно, вы можете получить его с помощью:

HttpContext context = CallContext.LogicalGetData("CurrentContextKey") as HttpContext;

Надеюсь, это поможет. Имейте в виду, что это обходное решение - это когда у вас нет выбора. Лучшей практикой является использование инъекции зависимостей.