这个问题在这里已经有了答案:Access the current HttpContext in ASP.NET Core (7 answers) Closed 5 years ago。
我们目前正在使用 ASP.NET Core 重写/转换我们的 ASP.NET WebForms 应用程序。尽量避免重新设计。
有一个部分我们在类库中使用 HttpContext
来检查当前状态。如何在 .NET Core 1.0 中访问 HttpContext.Current
?
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)}";
IOptions<T>
HttpContext.Current
,无论他们是否应该。询问 ASP.NET Core 中的等价物是什么,我认为没有太大的危害。答案可能是没有或有不同/更好的方法。
作为一般规则,将 Web 窗体或 MVC5 应用程序转换为 ASP.NET Core 将需要大量的重构。
HttpContext.Current
已在 ASP.NET Core 中删除。从单独的类库访问当前的 HTTP 上下文是 ASP.NET Core 试图避免的混乱架构类型。有几种方法可以在 ASP.NET Core 中重新构建它。
HttpContext 属性
您可以通过任何控制器上的 HttpContext
属性访问当前 HTTP 上下文。最接近原始代码示例的方法是将 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 管道编写 custom middleware,则当前请求的 HttpContext
会自动传递到您的 Invoke
方法中:
public Task Invoke(HttpContext context)
{
// Do something with the current HTTP context...
}
HTTP 上下文访问器
最后,您可以使用 IHttpContextAccessor
帮助程序服务来获取由 ASP.NET Core 依赖项注入系统管理的任何类中的 HTTP 上下文。当您有控制器使用的公共服务时,这很有用。
在您的构造函数中请求此接口:
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...
}
死灵术。是的,你可以,这就是方法。给那些迁移大型垃圾代码块的人的秘密提示:以下方法是黑客的邪恶痈肿,它积极参与执行撒旦的快速工作(在 .NET Core 框架开发人员的眼中),但它有效:
在 public class Startup
添加属性
public IConfigurationRoot Configuration { get; }
然后在ConfigureServices中为DI添加一个单例IHttpContextAccessor。
// 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>();
然后在配置中
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
}
现在在添加 IServiceProvider svp
的配置中,将此服务提供者保存到刚刚创建的虚拟类 System.Web.HttpContext (System.Web.HttpContext.ServiceProvider) 中的静态变量“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 中一样,当您尝试访问没有 HttpContext 时,您将获得 NullReference,例如它曾经位于 global.asax 中的 Application_Start
中。
我再次强调,这仅在您实际添加时才有效
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
就像我写的那样,你应该。
欢迎使用 DI 模式中的 ServiceLocator 模式 ;)
对于风险和副作用,请咨询您的常驻医生或药剂师 - 或在 github.com/aspnet 研究 .NET Core 的来源,并进行一些测试。
也许更易于维护的方法是添加这个帮助类
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;
}
}
}
}
然后在 Startup->Configure 中调用 HttpContext.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>()
);
services.AddSingleton
部分
如果您确实需要对当前上下文的静态访问,则有一个解决方案。在 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;
我希望这会有所帮助。请记住,此解决方法是在您别无选择时。最佳实践是使用 de 依赖注入。
不定期副业成功案例分享
IHttpContextAccessor
,但由于 RC2,它默认没有注册,因为它会带来一些每个请求的开销。查看公告github.com/aspnet/Announcements/issues/190