由于异步,在 WebApi 中使用 HttpContext.Current 是危险的 [英] Using HttpContext.Current in WebApi is dangerous because of async
问题描述
我的问题与此有点相关:HttpContext 的 WebApi 等效项. 依赖注入的项目.
我们想使用 Ninject 在 WebApi 区域中使用 HttpContext.Current 注入一个类.
我担心的是,这可能非常危险,因为在 WebApi 中(一切?)都是异步的.
如果我在这些方面有错误,请纠正我,这是我目前调查的内容:
HttpContext.Current 通过 Thread 获取当前上下文(我直接查看了实现).
在异步任务中使用 HttpContext.Current 是不可能的,因为它可以在另一个线程上运行.
WebApi 使用 IHttpController 和方法
Task
=>每个请求都是异步的 =>你不能在 action 方法中使用 HttpContext.Current .甚至可能发生,因为巧合在同一线程上执行了更多的请求.;ExecuteAsync 为了创建将内容注入构造函数的控制器
IHttpControllerActivator
与sync 方法IHttpController Create
一起使用.这就是 ninject 创建 Controller 及其所有依赖项的地方.
如果我在所有这 4 点中都正确,在操作方法或下面的任何层中使用
HttpContext.Current
是非常危险的,并且可能会产生意想不到的结果.我在 StackOverflow 上看到很多公认的答案都暗示了这一点.在我看来,这可以工作一段时间,但会在负载下失败.但是当使用 DI 创建控制器及其依赖项时,它是可以的,因为它运行在一个单独的线程上.我可以从构造函数中的 HttpContext 获取一个值,它会安全吗?.我想知道是否每个控制器都是在单个线程上为每个请求创建的,因为这可能会在高负载下导致问题,在这种情况下,来自 IIS 的所有线程都可能被消耗.
只是为了解释为什么我要注入 HttpContext 的东西:
- 一种解决方案是在控制器操作方法中获取请求,并将所需的值作为参数传递给所有层,直到它在代码深处的某个地方使用.
- 我们想要的解决方案:之间的所有层都不受此影响,我们可以在代码深处的某处使用注入的请求(例如,在某些依赖于 URL 的
ConfigurationProvider
中)
如果我完全错了或者我的建议是正确的,请给我你的意见,因为这个主题似乎很复杂.
HttpContext.Current 通过 Thread 获取当前上下文(我直接查看了实现).
说HttpContext
应用于线程会更正确;或者一个线程进入"HttpContext
.
在异步任务中使用 HttpContext.Current 是不可能的,因为它可以在另一个线程上运行.
完全没有;async
/await
的默认行为将在任意线程上恢复,但该线程将在恢复您的 async
方法之前进入请求上下文.
其中的关键是 SynchronizationContext
.如果您不熟悉,我有一篇有关该主题的 MSDN 文章.SynchronizationContext
为平台定义了一个上下文",常见的有 UI 上下文(WPF、WinPhone、WinForms 等)、线程池上下文和 ASP.NET 请求上下文.>
ASP.NET 请求上下文管理 HttpContext.Current
以及其他一些内容,例如文化和安全性.UI 上下文都与单个线程( UI 线程)紧密关联,但 ASP.NET 请求上下文不绑定到特定线程.但是,它一次只允许请求上下文中的一个线程.
解决方案的另一部分是 async
和 await
如何工作.我的博客上有一个 async
介绍,描述了他们的行为.总之,默认情况下 await
将捕获当前上下文(即 SynchronizationContext.Current
除非它是 null
),并使用该上下文恢复async
方法.因此,await
会自动捕获 ASP.NET SynchronizationContext
并将在该请求上下文中恢复 async
方法(从而保留文化、安全性和HttpContext.Current
).
如果您await
ConfigureAwait(false)
,那么您明确告诉await
不要捕获上下文.
请注意,ASP.NET 确实必须更改其 SynchronizationContext
才能与 async
/await
一起正常工作.您必须确保应用程序是针对 .NET 4.5 和 也在其 web.config 中明确针对 4.5;这是新 ASP.NET 4.5 项目的默认设置,但如果您从 ASP.NET 4.0 或更早版本升级现有项目,则必须明确设置.
您可以通过针对 .NET 4.5 执行您的应用程序并观察 SynchronizationContext.Current
来确保这些设置是正确的.如果它是AspNetSynchronizationContext
,那么你很好;如果是LegacyAspNetSynchronizationContext
,则设置错误.
只要设置正确(并且您使用的是 ASP.NET 4.5 AspNetSynchronizationContext
),那么您就可以在 之后安全地使用
,无需担心.HttpContext.Current
等待
My question is a bit related to this: WebApi equivalent for HttpContext.Items with Dependency Injection.
We want to inject a class using HttpContext.Current in WebApi area using Ninject.
My concern is, this could be very dangerous, as in WebApi (everything?) is async.
Please correct me if I am wrong in these points, this is what I investigated so far:
HttpContext.Current gets the current context by Thread (I looked into the implementation directly).
Using HttpContext.Current inside of async Task is not possible, because it can run on another Thread.
WebApi uses IHttpController with method
Task<HttpResponseMessage> ExecuteAsync
=> every request is async => you cannot use HttpContext.Current inside of action method. It could even happen, more Request are executed on the same thread by coicidence.For creating controllers with injected stuff into constructors
IHttpControllerActivator
is used with sync methodIHttpController Create
. This is, where ninject creates Controller with all its dependencies.
If I am correct in all of these 4 points, using of
HttpContext.Current
inside of an action method or any layer below is very dangerous and can have unexpected results. I saw on StackOverflow lot of accepted answers suggesting exactly this. In my opinion this can work for a while, but will fail under load.But when using DI to create a Controller and its dependencies, it is Ok, because this runs on one separated thread. I could get a value from the HttpContext in the constructor and it would be safe?. I wonder if each Controller is created on single thread for every request, as this could cause problem under heavy loads, where all threads from IIS could be consumed.
Just to explain why I want to inject HttpContext stuff:
- one solution would be to get the request in controller action method and pass the needed value all the layers as param until its used somewhere deep in the code.
- our wanted solution: all the layers between are not affected by this, and we can use the injected request somewhere deep in code (e.g. in some
ConfigurationProvider
which is dependent on URL)
Please give me your opinion if I am totally wrong or my suggestions are correct, as this theme seems to be very complicated.
HttpContext.Current gets the current context by Thread (I looked into the implementation directly).
It would be more correct to say that HttpContext
is applied to a thread; or a thread "enters" the HttpContext
.
Using HttpContext.Current inside of async Task is not possible, because it can run on another Thread.
Not at all; the default behavior of async
/await
will resume on an arbitrary thread, but that thread will enter the request context before resuming your async
method.
The key to this is the SynchronizationContext
. I have an MSDN article on the subject if you're not familiar with it. A SynchronizationContext
defines a "context" for a platform, with the common ones being UI contexts (WPF, WinPhone, WinForms, etc), the thread pool context, and the ASP.NET request context.
The ASP.NET request context manages HttpContext.Current
as well as a few other things such as culture and security. The UI contexts are all tightly associated with a single thread (the UI thread), but the ASP.NET request context is not tied to a specific thread. It will, however, only allow one thread in the request context at a time.
The other part of the solution is how async
and await
work. I have an async
intro on my blog that describes their behavior. In summary, await
by default will capture the current context (which is SynchronizationContext.Current
unless it is null
), and use that context to resume the async
method. So, await
is automatically capturing the ASP.NET SynchronizationContext
and will resume the async
method within that request context (thus preserving culture, security, and HttpContext.Current
).
If you await
ConfigureAwait(false)
, then you're explicitly telling await
to not capture the context.
Note that ASP.NET did have to change its SynchronizationContext
to work cleanly with async
/await
. You have to ensure that the application is compiled against .NET 4.5 and also explicitly targets 4.5 in its web.config; this is the default for new ASP.NET 4.5 projects but must be explicitly set if you upgraded an existing project from ASP.NET 4.0 or earlier.
You can ensure these settings are correct by executing your application against .NET 4.5 and observing SynchronizationContext.Current
. If it is AspNetSynchronizationContext
, then you're good; if it's LegacyAspNetSynchronizationContext
, then the settings are wrong.
As long as the settings are correct (and you are using the ASP.NET 4.5 AspNetSynchronizationContext
), then you can safely use HttpContext.Current
after an await
without worrying about it.
这篇关于由于异步,在 WebApi 中使用 HttpContext.Current 是危险的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!