在WebApi中使用HttpContext.Current是非常危险的,因为是asynchronous的

我的问题是有点相关: WebApi相当于HttpContext.Items与dependency injection 。

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

我担心的是,这可能是非常危险的 ,因为在WebApi( 一切? )是asynchronous的。

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

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

  2. 使用HttpContext.Current里面的async Task是不可能的,因为它可以在另一个Thread上运行。

  3. WebApi使用IHttpController和方法Task<HttpResponseMessage> ExecuteAsync =>每个请求都是async =>你不能使用HttpContext.Current里面的action方法。 它甚至可能发生,更多的请求由同一个线程执行。

  4. 创build控制器与注入东西到构造函数IHttpControllerActivator与同步方法IHttpController Create 。 这是ninject创buildController的所有依赖项的地方。


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

  • 但是,当使用DI来创build一个控制器和它的依赖关系时,这是正常的,因为它运行在一个单独的线程上。 我可以从构造函数中的HttpContext获得一个值,这将是安全的? 。 我想知道是否每个控制器都是在单个线程上为每个请求创build的,因为这可能会导致在负载很重的情况下可能会消耗来自IIS的所有线程的问题。

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

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

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

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

HttpContext应用于一个线程是更正确的; 或者一个线程“进入” HttpContext

使用HttpContext.Current里面的async Task是不可能的,因为它可以在另一个Thread上运行。

一点也不; async / await的默认行为将在任意线程上恢复,但是在恢复async方法之前,该线程将进入请求上下文。


关键是SynchronizationContext 。 如果您不熟悉这个主题,我有关于这个主题的MSDN文章 。 一个SynchronizationContext定义了一个平台的“上下文”,常用的是UI上下文(WPF,WinPhone,WinForms等),线程池上下文和ASP.NET请求上下文。

ASP.NET请求上下文pipe理HttpContext.Current以及一些其他的东西,如文化和安全。 UI上下文都与单个线程(UI线程)紧密关联,但是ASP.NET请求上下文不绑定到特定的线程。 但是,它一次只允许请求上下文中有一个线程。

解决scheme的另一部分是如何asyncawait工作。 我在我的博客上描述了他们的行为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 ),则可以在await后安全地使用HttpContext.Current ,而不必担心。

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

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

只有当您返回任务或任务<T>时,ASP.NET Web API操作(以及所有pipe道方法)才会被asynchronous调用。 这听起来很明显,但没有任何带有Async后缀的pipe道方法将在其自己的线程中运行。 使用毯子asynchronous可能是一个用词不当。 [更新:ASP.NET团队确实已经确认,Async用于表示返回任务的方法,并且可以asynchronous运行但不必]

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

我为此创build了一个小testing应用程序,如下所示:

 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}; } } 

和一个asynchronous版本返回async Task<object>

我试图用jQuery做一个DOS的攻击,并不能确定任何问题,直到我用await Task.Delay(1).ConfigureAwait(false); ,这显然会失败。

我从文章中得到的是,问题非常复杂,使用asynchronous操作方法时会发生线程切换,所以在操作方法调用的代码的任何地方使用HttpContext.Current并不是一个好主意。 但是,由于控制器是同步创build的,所以在构造函数中使用HttpContext.Current以及在dependency injection中都可以。

当有人对这个问题有另一个解释时,请纠正我,因为这个问题非常复杂,我还没有100%的信服。

diclaimer:

  • 我现在忽略自承载Web-Api的问题,IIS,其中HttpContext.Current可能无法正常工作。 我们现在依靠IIS。

我正在使用一个Web API,这是使用asynchronous/等待方法。

也使用

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

这工作很好,时间很长,突然之间没有代码的变化。

花费很多时间回到以前的旧版本,发现丢失的关键导致这个问题。

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

我在技术上不是专家。 但是我build议在你的webconfiguration文件中添encryption钥,并给它一个GO