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

Multithreading 在WebApi中使用HttpContext.Current是危险的,因为异步,multithreading,asynchronous,asp.net-web-api,ninject,httpcontext,Multithreading,Asynchronous,Asp.net Web Api,Ninject,Httpcontext,我的问题与此有点相关: 我们想使用Ninject在WebApi区域中使用HttpContext.Current注入一个类 我担心的是,这可能是非常危险的,因为WebApi(一切?)是异步的 如果我在这几点上有错误,请纠正我,这是我迄今为止调查的内容: Current通过线程获取当前上下文(我直接查看了实现) 无法在异步任务内部使用HttpContext.Current,因为它可以在另一个线程上运行 WebApi使用IHttpController和方法Task ExecuteAsync=>每个

我的问题与此有点相关:

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

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

如果我在这几点上有错误,请纠正我,这是我迄今为止调查的内容:

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

  • 无法在异步任务内部使用HttpContext.Current,因为它可以在另一个线程上运行

  • WebApi使用IHttpController和方法
    Task ExecuteAsync
    =>每个请求都是异步的=>您不能使用HttpContext.Current内部的action方法。这种情况甚至可能发生,更多的请求在同一线程上被执行

  • 用于创建将注入内容注入构造函数的控制器
    IHttpControllerActivator
    sync方法
    ihttpcontrollercreate
    一起使用。这就是ninject创建控制器及其所有依赖项的地方


    • 如果我在这四点上都是正确的,那么在操作方法或下面的任何层中使用
      HttpContext.Current
      是非常危险的,可能会产生意外的结果。我在网上看到了很多公认的答案,正是这一点。在我看来,这可以工作一段时间,但在负载下会失败

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

    为了解释我为什么要注入HttpContext内容:

    • 一种解决方案是在controlleraction方法中获取请求,并将所需的值作为param传递给所有层,直到它在代码的深处被使用
    • 我们想要的解决方案:层与层之间的所有层都不受此影响,我们可以在代码深处的某个地方使用注入的请求(例如,在一些依赖于URL的
      ConfigurationProvider
      中)

    如果我完全错了,或者我的建议是正确的,请给出你的意见,因为这个主题似乎非常复杂。

    我发现了一篇非常好的文章,正好描述了这个问题:

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

    仅当返回一个或多个任务时,才会异步调用ASP.NET Web API操作(以及所有管道方法)。这听起来可能很明显,但是没有一个带有异步后缀的管道方法会在它们自己的线程中运行。使用blanket-Async可能用词不当。[更新:ASP.NET团队确实已经确认,Async用于表示返回任务的方法,可以异步运行,但不必如此]

    我从文章中了解到,动作方法是同步调用的,但这是调用方的决定

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

    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};
        }
    }
    
    一个异步版本返回
    异步任务

    我试图用jquery对它进行一点DOS攻击,直到我使用
    wait Task.Delay(1)、configurewait(false)之后,我才确定是否存在任何问题,很明显它会失败

    我从这篇文章中了解到,问题非常复杂,在使用异步操作方法时可能会发生线程切换,因此使用HttpContext.Current在操作方法调用的代码中的任何地方都是一个好主意。但是由于控制器是同步创建的,在构造函数和依赖项注入中使用HttpContext.Current是可以的

    当有人对这个问题有另一种解释时,请纠正我,因为这个问题非常复杂,我仍然不是100%相信

    迪克莱默:

    • 我现在忽略了没有IIS的自托管Web Api的问题,其中HttpContext.Current可能无论如何都无法工作。我们现在依赖IIS
    Current通过线程获取当前上下文(我直接查看了实现)

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

    无法在异步任务内部使用HttpContext.Current,因为它可以在另一个线程上运行

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


    这一点的关键是
    SynchronizationContext
    。如果你不熟悉的话,我有个建议。
    SynchronizationContext
    为平台定义了一个“上下文”,常见的是UI上下文(WPF、WinPhone、WinForms等)、线程池上下文和ASP.NET请求上下文

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

    解决方案的另一部分是
    async
    wait
    如何工作。我有一本书。总之,
    await
    默认情况下将捕获当前上下文(即
    SynchronizationContext.current
    ,除非它是
    null
    ),并使用该上下文恢复
    async
    方法。因此,
    await
    会自动捕获ASP.NET
    synchronizationcontet
    
    1) HttpContext.Current.Server.MapPath
    2) System.Web.HttpContext.Current.Request.ServerVariables
    
    < httpRuntime targetFramework="4.5.2"  /> under system.web