在Web Api中使用HttpContext.Current是危险的,因为异步

时间:2014-07-25 12:47:56

标签: multithreading asynchronous asp.net-web-api ninject httpcontext

我的问题与此有点相关:WebApi equivalent for HttpContext.Items with Dependency Injection

我们想使用Ninject在WebApi区域中使用HttpContext.Current注入一个类。

我担心的是,这可能是非常危险,因为在WebApi(所有内容?)是异步的。

如果我在这些问题上错了,请纠正我,这是我到目前为止调查的内容:

  1. HttpContext.Current通过Thread获取当前上下文(我直接查看了实现)。

  2. 在异步任务中使用HttpContext.Current是不可能的,因为它可以在另一个线程上运行。

  3. WebApi使用IHttpController方法Task<HttpResponseMessage> ExecuteAsync =&gt;每个请求都是async =&gt;你不能在action方法中使用HttpContext.Current。它甚至可能发生,更多的请求是通过相同的线程在同一个线程上执行的。

  4. 为了将带有注入内容的控制器创建到构造函数中,IHttpControllerActivator与 sync 方法IHttpController Create一起使用。这就是ninject创建Controller及其所有依赖项的地方。


    • 如果我在所有这四点中都是正确的,那么在动作方法或下面任何一层中使用HttpContext.Current是非常危险的,并且会产生意想不到的结果。我看到很多公认的答案正是如此。恕我直言,这可以工作一段时间,但会在负载下失败。

    • 但是当使用DI创建一个Controller及其依赖项时,它是好的,因为它在一个独立的线程上运行。 我可以从构造函数中的HttpContext获取一个值,它会安全吗?。我想知道是否每个请求都在单个线程上创建了Controller,因为这可能会导致重负载下的问题,其中可以使用来自IIS的所有线程。

    只是为了解释我为什么要注入HttpContext的东西:

    • 一个解决方案是在控制器操作方法中获取请求,并将所有层的所需值作为参数传递,直到在代码深处使用它为止。
    • 我们想要的解决方案:它们之间的所有层都没有受到影响,我们可以在代码深处的某个地方使用注入的请求(例如,在一些依赖于URL的ConfigurationProvider中)

      如果我完全错误或者我的建议是正确的,请告诉我你的意见,因为这个主题似乎非常复杂。 Thx提前!

3 个答案:

答案 0 :(得分:96)

  

HttpContext.Current通过Thread获取当前上下文(我直接查看了实现)。

HttpContext应用于线程更为正确;或线程&#34;进入&#34; HttpContext

  

在异步任务中使用HttpContext.Current是不可能的,因为它可以在另一个线程上运行。

完全没有; async / await的默认行为将在任意线程上恢复,但该线程将在恢复async方法之前进入请求上下文。


关键是SynchronizationContext。如果您不熟悉,我会MSDN article on the subjectSynchronizationContext定义了一个&#34; context&#34;对于一个平台,常见的是UI上下文(WPF,WinPhone,WinForms等),线程池上下文和ASP.NET请求上下文。

ASP.NET请求上下文管理HttpContext.Current以及文化和安全等其他一些内容。 UI上下文都与单个线程( UI线程)紧密关联,但ASP.NET请求上下文与特定线程无关。但是,它一次只允许请求上下文中的一个线程

解决方案的另一部分是asyncawait的工作原理。我有async intro on my blog that describes their behavior。总之,await默认情况下会捕获当前上下文(SynchronizationContext.Current除非它是null),并使用该上下文恢复async方法。因此,await会自动捕获ASP.NET SynchronizationContext,并将在该请求上下文中恢复async方法(从而保留文化,安全性和HttpContext.Current)。

如果您await ConfigureAwait(false),那么您明确告诉await 捕获上下文。

请注意,ASP.NET必须更改其SynchronizationContext以使用async / await干净利落地工作。您必须确保应用程序是针对.NET 4.5和also explicitly targets 4.5 in its web.config编译的;这是新ASP.NET 4.5项目的默认设置,但如果从ASP.NET 4.0或更早版本升级现有项目,则必须显式设置。

通过针对.NET 4.5执行应用程序并观察SynchronizationContext.Current,可以确保这些设置正确无误。如果是AspNetSynchronizationContext,那么你就是好人;如果它是LegacyAspNetSynchronizationContext,那么设置是错误的。

只要设置正确(并且您使用的是ASP.NET 4.5 AspNetSynchronizationContext),就可以在HttpContext.Current之后安全地使用await,而无需担心。< / p>

答案 1 :(得分:4)

我正在使用web api,它正在使用async / await方法。

也使用

1) HttpContext.Current.Server.MapPath
2) System.Web.HttpContext.Current.Request.ServerVariables

这项工作正常,因为没有代码更改而突然中断。

通过恢复到以前的旧版本而花费大量时间,发现丢失的密钥会导致问题。

< httpRuntime targetFramework="4.5.2"  /> under system.web

我不是技术专家。但我建议将密钥添加到您的网络配置中并为其指定 GO

答案 2 :(得分:2)

我发现非常好的文章正好描述了这个问题:http://byterot.blogspot.cz/2012/04/aspnet-web-api-series-part-3-async-deep.html?m=1

作者深入研究了如何在WebApi框架中调用ExecuteAsync方法并得出这样的结论:

仅当您返回任务或任务&lt; T&gt;时,才会异步调用ASP.NET Web API操作(以及所有管道方法)。这可能听起来很明显,但是没有带有Async后缀的管道方法都会在自己的线程中运行。使用毯式异步可能是用词不当。 [更新:ASP.NET团队确实已经确认Async用于表示返回Task的方法,并且可以异步运行但不必运行]

我从文章中了解到,Action方法是同步调用的,但它是调用者的决定。

我为此创建了一个小型测试应用程序,如下所示:

public class ValuesController : ApiController
{
    public object Get(string clientId, string specialValue)
    {
        HttpRequest staticContext = HttpContext.Current.Request;
        string staticUrl = staticContext.Url.ToString();

        HttpRequestMessage dynamicContext = Request;
        string dynamicUrl = dynamicContext.RequestUri.ToString();

        return new {one = staticUrl, two = dynamicUrl};
    }
}

和一个返回async Task<object>

的异步版本

我试图用jquery对它进行一点DOS攻击,直到我使用await Task.Delay(1).ConfigureAwait(false);才能确定任何问题,这显然会失败。

我从文章中得到的是,问题非常复杂,并且在使用异步操作方法时可能会发生Thread切换,所以在任何地方使用HttpContext.Current肯定是 NOT 一个好主意从动作方法调用的代码。但是,由于控制器是同步创建的,因此在构造函数中使用HttpContext.Current以及依赖注入也是可以的。

如果有人对此问题有另一种解释,请纠正我,因为这个问题非常复杂,我仍然不是100%确信。

diclaimer:

  • 我现在忽略了自托管的Web-Api withoud IIS的问题,其中HttpContext.Current无论如何都无法工作。我们现在依赖IIS。