Potrzebuję dostępu do prądu HttpContext
w metodzie statycznej lub usłudze narzędziowej.
W przypadku klasycznego ASP.NET MVC i System.Web
po prostu używałbym HttpContext.Current
statycznego dostępu do kontekstu. Ale jak to zrobić w ASP.NET Core?
Potrzebuję dostępu do prądu HttpContext
w metodzie statycznej lub usłudze narzędziowej.
W przypadku klasycznego ASP.NET MVC i System.Web
po prostu używałbym HttpContext.Current
statycznego dostępu do kontekstu. Ale jak to zrobić w ASP.NET Core?
Odpowiedzi:
HttpContext.Current
nie istnieje już w ASP.NET Core, ale jest nowy IHttpContextAccessor
, który można wstrzyknąć w zależnościach i użyć do pobrania bieżącego 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
, aby rozwiązać usługę, nawet z nie-DI-wtryskiwanego przykład: CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>()
. W praktyce to super, jeśli można tego uniknąć :)
Nekromancja.
TAK, MOŻESZ
Tajna wskazówka dla tych, którzy migrują w dużych ilościachdżonkifragmenty (westchnienie, wpadka Freuda) kodu.
Poniższa metoda jest złym hackiem, który aktywnie angażuje się w wykonywanie ekspresowej pracy szatana (w oczach twórców frameworka .NET Core), ale działa :
W public class Startup
dodać właściwość
public IConfigurationRoot Configuration { get; }
Następnie dodaj singleton IHttpContextAccessor do DI w 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>();
Następnie w Konfiguruj
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
)
{
dodaj parametr DI IServiceProvider svp
, więc metoda wygląda następująco:
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
,IServiceProvider svp)
{
Następnie utwórz klasę zastępczą dla 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
}
Teraz w programie Configure, gdzie dodano IServiceProvider svp
, zapisz tego dostawcę usług w zmiennej statycznej „ServiceProvider” w właśnie utworzonej klasie fikcyjnej System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)
i ustaw HostingEnvironment.IsHosted na true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
to jest w zasadzie to, co zrobił System.Web, tyle że nigdy go nie widziałeś (myślę, że zmienna została zadeklarowana jako wewnętrzna, a nie publiczna).
// 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
});
Podobnie jak w ASP.NET Web-Forms, otrzymasz NullReference, gdy próbujesz uzyskać dostęp do HttpContext, gdy nie ma go, na przykład Application_Start
w global.asax.
Podkreślam jeszcze raz, działa to tylko wtedy, gdy faktycznie dodałeś
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
tak jak napisałem, powinieneś.
Witamy we wzorcu ServiceLocator we wzorcu DI;)
W przypadku zagrożeń i skutków ubocznych zapytaj swojego lekarza lub farmaceutę - lub zapoznaj się z źródłami .NET Core na github.com/aspnet i przeprowadź testy.
Być może łatwiejszą w utrzymaniu metodą byłoby dodanie tej klasy pomocniczej
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;
}
}
}
}
A następnie wywołanie HttpContext.Configure w 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>()
);
Aby dodać do innych odpowiedzi ...
W ASP.NET 2.1 Rdzeń, tam metodę rozszerzenia , które zarejestruje z prawidłowym życia:AddHttpContextAccessor
IHttpContextAccessor
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
// Other code...
}
Najbardziej legalnym sposobem, jaki wymyśliłem, było wstrzyknięcie IHttpContextAccessor do Twojej statycznej implementacji w następujący sposób:
public static class HttpHelper
{
private static IHttpContextAccessor _accessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_accessor = httpContextAccessor;
}
public static HttpContext HttpContext => _accessor.HttpContext;
}
Następnie przypisanie IHttpContextAccessor w Startup Configure powinno wykonać zadanie.
HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
Chyba należy również zarejestrować singleton usługi:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Zgodnie z tym artykułem: Dostęp do HttpContext poza składnikami platformy w 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;
}
}
}
Następnie:
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;
}
}
Następnie:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app)
{
app.UseStaticHttpContext();
app.UseMvc();
}
}
Możesz go używać w ten sposób:
using System.Web;
public class MyService
{
public void DoWork()
{
var context = HttpContext.Current;
// continue with context instance
}
}
W Startup
services.AddHttpContextAccessor();
W kontrolerze
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
byłoby to dostępne tylko w miejscach, w których kontener DI rozwiązuje instancję.