Come ottenere HttpContext.Current in ASP.NET Core?

Al momento stiamo riscrivendo / convertendo la nostra applicazione ASP.NET WebForms utilizzando ASP.NET Core. Cercando di evitare la reingegnerizzazione il più ansible.

C’è una sezione in cui utilizziamo HttpContext in una libreria di classi per verificare lo stato corrente. Come posso accedere a HttpContext.Current in .NET Core 1.0?

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

Ho bisogno di accedere a questo per build l’host dell’applicazione corrente.

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

Come regola generale, la conversione di un’applicazione Web Form o MVC5 in ASP.NET Core richiede una quantità significativa di refactoring.

HttpContext.Current stato rimosso in ASP.NET Core. L’accesso al contesto HTTP corrente da una libreria di classi separata è il tipo di architettura disordinata che ASP.NET Core tenta di evitare. Esistono alcuni modi per ri-progettare questo in ASP.NET Core.

Proprietà HttpContext

È ansible accedere al contesto HTTP corrente tramite la proprietà HttpContext su qualsiasi controller. La cosa più vicina all’esempio di codice originale sarebbe passare HttpContext nel metodo che stai chiamando:

 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 } 

Parametro HttpContext nel middleware

Se stai scrivendo un middleware personalizzato per la pipeline di ASP.NET Core, HttpContext della richiesta corrente viene passato automaticamente al tuo metodo Invoke :

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

Accessor di contesto HTTP

Infine, è ansible utilizzare il servizio helper IHttpContextAccessor per ottenere il contesto HTTP in qualsiasi class gestita dal sistema di input delle dipendenze ASP.NET Core. Ciò è utile quando si dispone di un servizio comune utilizzato dai controller.

Richiedi questa interfaccia nel tuo costruttore:

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

È quindi ansible accedere al contesto HTTP corrente in modo sicuro:

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

IHttpContextAccessor non viene sempre aggiunto al contenitore del servizio per impostazione predefinita, quindi registrarlo in ConfigureServices solo per sicurezza:

 public void ConfigureServices(IServiceCollection services) { services.TryAddSingleton(); // Other code... } 

Necromancing.
SI PUOI, ed è così.
Un consiglio segreto per coloro che migrano in grande giunche pezzi di codice:
Il seguente metodo è un diabolico frammento di un hack che è triggersmente impegnato nello svolgere il lavoro express di satana (agli occhi degli sviluppatori di .NET Framework), ma funziona :

Nella public class Startup

aggiungi una proprietà

 public IConfigurationRoot Configuration { get; } 

Quindi aggiungere un singleton IHttpContextAccessor a DI in ConfigureServices.

  // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); 

Quindi in Configura

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

aggiungere il parametro DI IServiceProvider svp , quindi il metodo ha il seguente aspetto:

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

Quindi, creare una class sostitutiva per 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(); 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 } 

Ora in Configura, dove hai aggiunto lo IServiceProvider svp , salva questo fornitore di servizi nella variabile statica “ServiceProvider” nella class fittizia appena creata System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)

e imposta HostingEnvironment.IsHosted su true

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

questo è essenzialmente ciò che ha fatto System.Web, solo che non l’hai mai visto (suppongo che la variabile sia stata dichiarata come interna anziché pubblica).

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

Come in ASP.NET Web-Forms, si otterrà un NullReference quando si sta tentando di accedere a HttpContext quando non ce ne sono, come ad esempio in Application_Start in global.asax.

Sottolineo ancora, questo funziona solo se in realtà hai aggiunto

 services.AddSingleton(); 

come ho scritto dovresti
Benvenuti nel pattern ServiceLocator all’interno del pattern DI;)
Per i rischi e gli effetti collaterali, chiedi al tuo medico o farmacista residente – o studia le fonti di .NET Core su github.com/aspnet e fai qualche test.


Forse un metodo più gestibile potrebbe aggiungere questa class helper

 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 quindi chiamando HttpContext.Configure in Avvio-> Configura

 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() ); 

C’è una soluzione a questo se hai davvero bisogno di un accesso statico al contesto attuale. In Startup.Configure (….)

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

E quando ne hai bisogno puoi ottenerlo con:

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

Spero che aiuti. Tieni presente che questa soluzione è quando non hai una scelta. La migliore pratica consiste nell’utilizzare l’iniezione di dipendenza.