Actualmente estamos reescribiendo / convirtiendo nuestra aplicación ASP.NET WebForms utilizando ASP.NET Core. Tratando de evitar la reingeniería tanto como sea posible.
Hay una sección donde usamos HttpContext
en una biblioteca de clases para verificar el estado actual. ¿Cómo puedo acceder a HttpContext.Current
en .NET Core 1.0?
var current = HttpContext.Current; if (current == null) { // do something here // string connection = Configuration.GetConnectionString("MyDb"); }
Necesito acceder a esto para 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)}";
Como regla general, la conversión de una aplicación Web Forms o MVC5 a ASP.NET Core requerirá una gran cantidad de refactorización.
HttpContext.Current
fue eliminado en ASP.NET Core. Acceder al contexto HTTP actual desde una biblioteca de clases separada es el tipo de architecture desordenada que ASP.NET Core intenta evitar. Hay algunas maneras de volver a diseñar esto en ASP.NET Core.
Puede acceder al contexto HTTP actual a través de la propiedad HttpContext
en cualquier controlador. Lo más parecido a su muestra de código original sería pasar HttpContext
al 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 }
Si está escribiendo un middleware personalizado para la tubería ASP.NET Core, el HttpContext
la solicitud actual se pasa automáticamente a su método Invoke
:
public Task Invoke(HttpContext context) { // Do something with the current HTTP context... }
Finalmente, puede usar el servicio IHttpContextAccessor
para obtener el contexto HTTP en cualquier clase que sea administrada por el sistema de dependency injections ASP.NET Core. Esto es útil cuando tiene un servicio común que usan sus controladores.
Solicite esta interfaz en su constructor:
public MyMiddleware(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; }
A continuación, puede acceder al contexto HTTP actual de una manera segura:
var context = _httpContextAccessor.HttpContext; // Do something with the current HTTP context...
IHttpContextAccessor
no siempre se agrega al contenedor de servicio de forma predeterminada, así que regístrelo en ConfigureServices
solo para estar seguro:
public void ConfigureServices(IServiceCollection services) { services.TryAddSingleton(); // Other code... }
Nigromancia
SÍ PUEDES, y así es como.
Un consejo secreto para aquellos que migran grandes juncos trozos de código:
El siguiente método es un carbunclo malvado de un truco que se dedica activamente a llevar a cabo el trabajo expreso de satanás (a los ojos de los desarrolladores del framework .NET Core), pero funciona :
En public class Startup
agregar una propiedad
public IConfigurationRoot Configuration { get; }
Y luego agregue un singleton IHttpContextAccessor 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();
Luego, en Configurar
public void Configure( IApplicationBuilder app ,IHostingEnvironment env ,ILoggerFactory loggerFactory ) {
agregue el DI IServiceProvider svp
, por lo que el método se ve 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(); 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 Configure, donde ha agregado 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 establecer HostingEnvironment.IsHosted a true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
esto es esencialmente lo que System.Web hizo, 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 NullReference cuando intente acceder a HttpContext cuando no haya ninguno, como solía ser en Application_Start
en global.asax.
Insisto nuevamente, esto solo funciona si realmente agregaste
services.AddSingleton();
como escribí, deberías.
Bienvenido al patrón ServiceLocator dentro del patrón DI;)
Para los riesgos y efectos secundarios, consulte a su médico o farmacéutico residente, o estudie las fonts de .NET Core en github.com/aspnet , y realice algunas pruebas.
Tal vez un método más fácil de mantener sería agregar esta clase de ayuda
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 llamar 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() );
Hay una solución para esto si realmente necesita un acceso estático al contexto actual. En Startup.Configure (…)
app.Use(async (httpContext, next) => { CallContext.LogicalSetData("CurrentContextKey", httpContext); try { await next(); } finally { CallContext.FreeNamedDataSlot("CurrentContextKey"); } });
Y cuando lo necesite, puede obtenerlo 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 usar dependency injection.