Using HttpContext.Current in WebApi is dangerous because of async

前端 未结 3 457
感动是毒
感动是毒 2020-11-27 11:03

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 We

相关标签:
3条回答
  • 2020-11-27 11:45

    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.

    0 讨论(0)
  • 2020-11-27 11:50

    I found very good article describing exactly this problem: http://byterot.blogspot.cz/2012/04/aspnet-web-api-series-part-3-async-deep.html?m=1

    author investigated deeply, how the ExecuteAsync method is called in the WebApi framework and came to this conclusion:

    ASP.NET Web API actions (and all the pipeline methods) will be called asynchronously only if you return a Task or Task<T>. This might sound obvious but none of the pipeline methods with Async suffix will run in their own threads. Using blanket Async could be a misnomer. [UPDATE: ASP.NET team indeed have confirmed that the Async is used to denote methods that return Task and can run asynchronously but do not have to]

    What I understood from the article is, that the Action methods are called synchronously, but it is the caller decision.

    I created a small test app for this purpose, something like this:

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

    and one Async version returning async Task<object>

    I tried to do a little DOS attack on it with jquery and could not determine any issue until I used await Task.Delay(1).ConfigureAwait(false);, which is obvious it would fail.

    What I took from the article is, that the problem is very complicated and Thread switch can happen when using async action method, so it is definetly NOT a good idea to use HttpContext.Current anywhere in the code called from the action methods. But as the controller is created synchronously, using HttpContext.Current in the constructor and as well in dependency injection is OK.

    When somebody has another explanation to this problem please correct me as this problem is very complicated an I am still not 100% convinced.

    diclaimer:

    • I ignore for now the problem of self-hosted Web-Api withoud IIS, where HttpContext.Current would not work probably anyway. We now rely on IIS.
    0 讨论(0)
  • 2020-11-27 11:56

    I am using a web api, which is using async/await methodology.

    also using

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

    This was working fine for a good amount of time which broke suddenly for no code change.

    Spending a lot of time by reverting back to previous old versions, found the missing key causes the issue.

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

    I am not an expert technically. But I suggest to add the key to your web config and give it a GO.

    0 讨论(0)
提交回复
热议问题