Necesito acceder a current HttpContext
en un método estático o un servicio de utilidad.
Con ASP.NET MVC clásico y System.Web
, solo lo usaría HttpContext.Current
para acceder al contexto de forma estática. Pero, ¿cómo hago esto en ASP.NET Core?
Necesito acceder a current HttpContext
en un método estático o un servicio de utilidad.
Con ASP.NET MVC clásico y System.Web
, solo lo usaría HttpContext.Current
para acceder al contexto de forma estática. Pero, ¿cómo hago esto en ASP.NET Core?
Respuestas:
HttpContext.Current
ya no existe en ASP.NET Core, pero hay una nueva IHttpContextAccessor
que puede inyectar en sus dependencias y usar para recuperar la actual HttpContext
:
public class MyComponent : IMyComponent
{
private readonly IHttpContextAccessor _contextAccessor;
public MyComponent(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public string GetDataFromSession()
{
return _contextAccessor.HttpContext.Session.GetString(*KEY*);
}
}
CallContextServiceLocator
de resolver un servicio, incluso desde una instancia inyecta la no-DI: CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>()
. En la práctica, es una gran cosa si puedes evitarlo :)
Nigromancia
SÍ PUEDES CONSEJO
secreto para aquellos que migran grandeschatarratrozos (suspiro, deslizamiento freudiano) de código.
El siguiente método es un malvado truco de un hack que se dedica activamente a llevar a cabo el trabajo expreso de satanás (a los ojos de los desarrolladores de .NET Core Framework), pero funciona :
En public 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ó el 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 true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
esto es esencialmente lo que hizo System.Web, solo que nunca lo viste (supongo que la variable se declaró 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 los formularios web ASP.NET, obtendrá una NullReference cuando intente acceder a un HttpContext cuando no haya ninguno, como solía estar Application_Start
en global.asax.
Insisto nuevamente, esto solo funciona si realmente agregaste
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
como te escribí deberías.
Bienvenido al patrón ServiceLocator dentro del patrón DI;)
Para conocer los riesgos y los efectos secundarios, consulte a su médico o farmacéutico residente, 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 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>()
);
Solo para agregar a las otras respuestas ...
En ASP.NET Core 2.1, existe el AddHttpContextAccessor
método de extensión , que registrará IHttpContextAccessor
con la vida útil correcta:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
// Other code...
}
La forma más legítima que se me ocurrió fue inyectando IHttpContextAccessor en su implementación estática de la siguiente manera:
public static class HttpHelper
{
private static IHttpContextAccessor _accessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_accessor = httpContextAccessor;
}
public static HttpContext HttpContext => _accessor.HttpContext;
}
Luego asignar el IHttpContextAccessor en la Configuración de inicio debería hacer el trabajo.
HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
Supongo que también deberías necesitar registrar el servicio singleton:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
De acuerdo con este artículo: Acceso a HttpContext fuera de los componentes del marco en ASP.NET Core
namespace System.Web
{
public static class HttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
}
Luego:
public static class StaticHttpContextExtensions
{
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
{
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
System.Web.HttpContext.Configure(httpContextAccessor);
return app;
}
}
Luego:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app)
{
app.UseStaticHttpContext();
app.UseMvc();
}
}
Puedes usarlo así:
using System.Web;
public class MyService
{
public void DoWork()
{
var context = HttpContext.Current;
// continue with context instance
}
}
En el inicio
services.AddHttpContextAccessor();
En el controlador
public class HomeController : Controller
{
private readonly IHttpContextAccessor _context;
public HomeController(IHttpContextAccessor context)
{
_context = context;
}
public IActionResult Index()
{
var context = _context.HttpContext.Request.Headers.ToList();
return View();
}
}
IHttpContextAccessor
solo estaría disponible en lugares donde el contenedor DI está resolviendo la instancia.