Как получить HttpContext.Current в ASP.NET Core?
В настоящее время мы переписываем/конвертируем наше приложение ASP.NET WebForms с использованием ASP.NET Core. Попытка избежать реинжиниринга как можно больше.
Существует раздел, в котором мы используем HttpContext
в библиотеке классов для проверки текущего состояния. Как я могу получить доступ к HttpContext.Current
в .NET Core 1.0?
var current = HttpContext.Current;
if (current == null)
{
// do something here
// string connection = Configuration.GetConnectionString("MyDb");
}
Мне нужно получить доступ к этому, чтобы создать текущий хост приложения.
$"{current.Request.Url.Scheme}://{current.Request.Url.Host}{(current.Request.Url.Port == 80 ? "" : ":" + current.Request.Url.Port)}";
Ответы
Ответ 1
Как правило, преобразование приложения Web Forms или MVC5 в ASP.NET Core потребует значительного объема рефакторинга.
HttpContext.Current
был удален в ASP.NET Core. Доступ к текущему HTTP-контексту из отдельной библиотеки классов - это тип грязной архитектуры, которую ASP.NET Core пытается избежать. Есть несколько способов перестроить это в ASP.NET Core.
Свойство HttpContext
Вы можете получить доступ к текущему HTTP-контексту через свойство HttpContext
на любом контроллере. Самым близким к вашему исходному HttpContext
кода будет передать HttpContext
в метод, который вы вызываете:
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
}
Параметр HttpContext в промежуточном программном обеспечении
Если вы пишете пользовательское промежуточное программное обеспечение для конвейера ASP.NET Core, текущий запрос HttpContext
передается в ваш метод Invoke
:
public Task Invoke(HttpContext context)
{
// Do something with the current HTTP context...
}
Средство доступа к контексту HTTP
Наконец, вы можете использовать вспомогательную службу IHttpContextAccessor
для получения HTTP-контекста в любом классе, который управляется системой внедрения зависимостей ASP.NET Core. Это полезно, когда у вас есть общая служба, используемая вашими контроллерами.
Запросите этот интерфейс в вашем конструкторе:
public MyMiddleware(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
Затем вы можете получить доступ к текущему HTTP-контексту безопасным способом:
var context = _httpContextAccessor.HttpContext;
// Do something with the current HTTP context...
IHttpContextAccessor
не всегда добавляется в контейнер службы по умолчанию, поэтому зарегистрируйте его в ConfigureServices
просто для безопасности:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
// if < .NET Core 2.2 use this
//services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Other code...
}
Ответ 2
Necromancing.
ДА ВЫ МОЖЕТЕ, и вот как.
Тайный совет для тех, кто переносит большие куски junks:
Следующий метод - злой карбункул взлома, который активно участвует в выполнении экспресс-работы сатаны (в глазах разработчиков платформы .NET Core), , но работает:
В public class Startup
добавить свойство
public IConfigurationRoot Configuration { get; }
И затем добавьте singleton IHttpContextAccessor в DI в 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>();
Затем в Configure
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
)
{
добавьте параметр DI IServiceProvider svp
, поэтому метод выглядит следующим образом:
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
,IServiceProvider svp)
{
Затем создайте класс замены для 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
}
Теперь в Configure, где вы добавили IServiceProvider svp
, сохраните этот поставщик услуг в статической переменной "ServiceProvider" только что созданного фиктивного класса System.Web.HttpContext(System.Web.HttpContext.ServiceProvider)
и установите HostingEnvironment.IsHosted в true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
это по сути то, что сделал System.Web, только что вы его никогда не видели (я думаю, переменная была объявлена как внутренняя, а не публичная).
// 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
});
Как и в ASP.NET Web-Forms, вы получите NullReference, когда вы пытаетесь получить доступ к HttpContext, когда его нет, например, он использовался в Application_Start
в global.asax.
Я снова подчеркиваю: это работает только если вы действительно добавили
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
как я написал, вы должны.
Добро пожаловать в шаблон ServiceLocator в шаблоне DI;)
Для рисков и побочных эффектов обратитесь к своему резидентному врачу или фармацевту или изучите источники .NET Core на github.com/aspnet и выполните некоторые тесты.
Возможно, более поддерживаемый метод будет добавлять этот вспомогательный класс
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;
}
}
}
}
И затем вызовите HttpContext.Configure в 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>()
);
Ответ 3
Это решение, если вам действительно нужен статический доступ к текущему контексту.
В Startup.Configure(....)
app.Use(async (httpContext, next) =>
{
CallContext.LogicalSetData("CurrentContextKey", httpContext);
try
{
await next();
}
finally
{
CallContext.FreeNamedDataSlot("CurrentContextKey");
}
});
И когда вам это нужно, вы можете получить его с помощью:
HttpContext context = CallContext.LogicalGetData("CurrentContextKey") as HttpContext;
Надеюсь, это поможет. Имейте в виду, что это обходное решение - это когда у вас нет выбора. Лучшей практикой является использование инъекции зависимостей.