¿Cómo obtener HttpContext.Current en ASP.NET Core? [duplicar]

Resuelto HaBo asked hace 8 años • 3 respuestas

Actualmente estamos reescribiendo/convirtiendo nuestra aplicación ASP.NET WebForms usando ASP.NET Core. Tratando de evitar en la medida de lo posible la reingeniería.

Hay una sección que usamos HttpContexten una biblioteca de clases para verificar el estado actual. ¿ Cómo puedo acceder HttpContext.Currenten .NET Core 1.0?

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

Necesito acceder a esto para poder construir el host de la aplicación actual.

$"{current.Request.Url.Scheme}://{current.Request.Url.Host}{(current.Request.Url.Port == 80 ? "" : ":" + current.Request.Url.Port)}";
HaBo avatar Jul 25 '16 21:07 HaBo
Aceptado

Como regla general, convertir una aplicación Web Forms o MVC5 a ASP.NET Core requerirá una cantidad significativa de refactorización.

HttpContext.Currentse eliminó en ASP.NET Core. Acceder al contexto HTTP actual desde una biblioteca de clases separada es el tipo de arquitectura desordenada que ASP.NET Core intenta evitar. Hay algunas formas de rediseñar esto en ASP.NET Core.

Propiedad HttpContext

Puede acceder al contexto HTTP actual a través de la HttpContextpropiedad en cualquier controlador. Lo más parecido a su ejemplo de código original sería pasar HttpContextal método que está llamando:

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
}

Parámetro HttpContext en middleware

Si está escribiendo middleware personalizado para la canalización de ASP.NET Core, las solicitudes actuales HttpContextse pasan a su Invokemétodo automáticamente:

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

Accesor de contexto HTTP

Finalmente, puede utilizar el IHttpContextAccessorservicio auxiliar para obtener el contexto HTTP en cualquier clase administrada por el sistema de inyección de dependencia ASP.NET Core. Esto es útil cuando tiene un servicio común que utilizan sus controladores.

Solicite esta interfaz en su constructor:

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

Luego podrá acceder al contexto HTTP actual de forma segura:

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

IHttpContextAccessorno siempre se agrega al contenedor de servicios de forma predeterminada, así que regístrelo ConfigureServicessolo para estar seguro:

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

    // Other code...
}
Nate Barbettini avatar Jul 25 '2016 17:07 Nate Barbettini

Nigromancia.
SÍ SE PUEDE, y así es como.
Un consejo secreto para quienes migran en grandechatarrafragmentos de código:
el siguiente método es un malvado carbunclo de un hack que participa activamente en llevar a cabo el trabajo expreso de satanás (a los ojos de los desarrolladores del marco .NET Core), pero funciona :

Enpublic class Startup

agregar una propiedad

public IConfigurationRoot Configuration { get; }

Y luego agregue un IHttpContextAccessor singleton a DI en 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>();

Luego en Configurar

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

agregue el parámetro DI IServiceProvider svp, para que el método se vea así:

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

A continuación, cree una clase de reemplazo para 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 


}

Ahora en Configurar, donde agregó IServiceProvider svp, guarde este proveedor de servicios en la variable estática "ServiceProvider" en la clase ficticia recién creada System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)

y establezca HostingEnvironment.IsHosted en verdadero

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

Esto es esencialmente lo que hizo System.Web, solo que nunca lo viste (supongo que la variable fue declarada como interna en lugar de pública).

// 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

    });

Al igual que en ASP.NET Web-Forms, obtendrá una referencia nula cuando intente acceder a un HttpContext cuando no hay ninguno, como solía estar en Application_Startglobal.asax.

Lo enfatizo nuevamente, esto solo funciona si realmente agregaste

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

como escribí que deberías.
Bienvenido al patrón ServiceLocator dentro del patrón DI;)
Para conocer los riesgos y efectos secundarios, consulte a su médico residente o farmacéutico, o estudie las fuentes de .NET Core en github.com/aspnet y realice algunas pruebas.


Quizás un método más fácil de mantener sería agregar esta clase auxiliar

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


    }


}

Y luego llamando a HttpContext.Configure en Inicio->Configurar

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>()
    );
Stefan Steiger avatar Oct 13 '2016 19:10 Stefan Steiger

Existe una solución para esto si realmente necesita un acceso estático al contexto actual. En Inicio.Configurar(….)

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

Y cuando lo necesites podrás conseguirlo con:

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

Espero que eso ayude. Tenga en cuenta que esta solución alternativa es cuando no tiene otra opción. La mejor práctica es utilizar la inyección de dependencia.

Hicham avatar Sep 19 '2016 10:09 Hicham