Preciso acessar a corrente HttpContext
em um método estático ou em um serviço utilitário.
Com o clássico ASP.NET MVC e System.Web
, eu usaria apenas HttpContext.Current
para acessar o contexto estaticamente. Mas como faço isso no ASP.NET Core?
Preciso acessar a corrente HttpContext
em um método estático ou em um serviço utilitário.
Com o clássico ASP.NET MVC e System.Web
, eu usaria apenas HttpContext.Current
para acessar o contexto estaticamente. Mas como faço isso no ASP.NET Core?
Respostas:
HttpContext.Current
não existe mais no ASP.NET Core, mas há um novo IHttpContextAccessor
que você pode injetar em suas dependências e usar para recuperar a corrente 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
resolver um serviço, mesmo a partir de uma instância não-DI-injetada: CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>()
. Na prática, é uma grande coisa se você pode evitá-lo :)
Necromante.
SIM PODE
Dica secreta para quem está migrando grandesjuncospedaços (suspiro, deslize freudiano) de código.
O método a seguir é um carbúnculo maligno de um hack que está envolvido ativamente na execução do trabalho expresso de satanás (aos olhos dos desenvolvedores de estrutura do .NET Core), mas funciona :
No public class Startup
adicionar uma propriedade
public IConfigurationRoot Configuration { get; }
E, em seguida, adicione um singleton IHttpContextAccessor à DI no 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>();
Em seguida, no Configure
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
)
{
adicione o parâmetro DI IServiceProvider svp
, para que o método se pareça com:
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
,IServiceProvider svp)
{
Em seguida, crie uma classe de substituição 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
}
Agora no Configure, onde você adicionou IServiceProvider svp
, salve este provedor de serviços na variável estática "ServiceProvider" na classe fictícia System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)
e defina HostingEnvironment.IsHosted como true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
isso é essencialmente o que System.Web fez, só que você nunca viu (acho que a variável foi declarada como interna em vez 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
});
Como nos formulários da Web do ASP.NET, você recebe um NullReference ao tentar acessar um HttpContext quando não houver nenhum, como costumava ser Application_Start
no global.asax.
Sublinho novamente, isso só funciona se você realmente adicionou
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
como eu escrevi você deveria.
Bem-vindo ao padrão ServiceLocator no padrão DI;)
Para riscos e efeitos colaterais, pergunte ao seu médico ou farmacêutico residente - ou estude as fontes do .NET Core em github.com/aspnet e faça alguns testes.
Talvez um método mais sustentável possa adicionar essa classe 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;
}
}
}
}
E, em seguida, chamando HttpContext.Configure em Inicialização-> 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>()
);
Apenas para adicionar às outras respostas ...
No ASP.NET Core 2.1, existe o AddHttpContextAccessor
método de extensão , que registrará o IHttpContextAccessor
tempo de vida correto:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
// Other code...
}
A maneira mais legítima que eu criei foi injetar IHttpContextAccessor na sua implementação estática da seguinte maneira:
public static class HttpHelper
{
private static IHttpContextAccessor _accessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_accessor = httpContextAccessor;
}
public static HttpContext HttpContext => _accessor.HttpContext;
}
Em seguida, atribuir o IHttpContextAccessor no Startup Configure deve fazer o trabalho.
HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
Eu acho que você também deve registrar o serviço singleton:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
De acordo com este artigo: Acessando o HttpContext fora dos componentes da estrutura no 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;
}
}
}
Então:
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;
}
}
Então:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app)
{
app.UseStaticHttpContext();
app.UseMvc();
}
}
Você pode usá-lo assim:
using System.Web;
public class MyService
{
public void DoWork()
{
var context = HttpContext.Current;
// continue with context instance
}
}
Na inicialização
services.AddHttpContextAccessor();
No 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
estaria disponível apenas em locais onde o contêiner de DI está resolvendo a instância.