Why is HttpContext.Current null after await?

C#asp.netasp.net Web-ApiAsync Await

C# Problem Overview


I have the following test WebAPI code, I don't use WebAPI in production but I made this because of a discussion I had on this question: WebAPI Async question

Anyways, here's the offending WebAPI method:

public async Task<string> Get(int id)
{
    var x = HttpContext.Current;
    if (x == null)
    {
        // not thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    await Task.Run(() => { Task.Delay(500); id = 3; });

    x = HttpContext.Current;
    if (x == null)
    {
        // thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    return "value";
}

I had hereto believed that the second exception is expected because when the await completes, it will likely be on a different thread where HttpContext.Current as a thread-static variable will no longer resolve to the appropriate value. Now, based on the synchronization context, it could actually be forced to go back to the same thread after the await but I'm not doing anything fancy in my test. This is just a plain, naive use of await.

In comments in another question I was told that HttpContext.Current should resolve after an await. There's even another comment on this question indicating the same. So what's true? Should it resolve? I think no, but I want an authoritative answer on this because async and await is new enough that I can't find anything definitive.

TL;DR: Is HttpContext.Current potentially null after an await?

C# Solutions


Solution 1 - C#

Please ensure you are writing an ASP.NET 4.5 application, and targeting 4.5. async and await have undefined behavior on ASP.NET unless you are running on 4.5 and are using the new "task-friendly" synchronization context.

In particular, this means you must either:

  • Set httpRuntime.targetFramework to 4.5, or
  • In your appSettings, set aspnet:UseTaskFriendlySynchronizationContext to true.

More information is available here.

Solution 2 - C#

As @StephenCleary correctly pointed out, you need this in your web.config:

<httpRuntime targetFramework="4.5" />

When I was first troubleshooting this, I did a solution-wide search for the above, confirmed it was present in all my web projects and quickly dismissed it as the culprit. Eventually it occurred to me to look at those search results in full context:

<!--
  For a description of web.config changes for .NET 4.5 see http://go.microsoft.com/fwlink/?LinkId=235367.

  The following attributes can be set on the <httpRuntime> tag.
    <system.Web>
      <httpRuntime targetFramework="4.5" />
    </system.Web>
-->

Doh.

Lesson: If you upgrade a web project to 4.5, you still need to get that setting in place manually.

Solution 3 - C#

I ran into this issue recently. As Stephen pointed out not setting explicitly the target framework can generate this issue.

In my case, our Web API was migrated to version 4.6.2 but the runtime target framework was never specified in the web config, so basically this was missing inside the tag:

If you have doubts about the framework version you are running this may help: Add the following line on any of your Web API methods and set a breakpoint to verify what type is currently loaded at runtime and verify it is not a Legacy implementation:

You should see this (AspNetSynchronizationContext):

enter image description here

Instead of LegazyAspNetSynchronizationContext (Which was what I saw before adding the target framework):

enter image description here

If you go to the source code (https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs) you will see that the Legacy implementation of this interface lacks of asynchronous support.

enter image description here

I spent a lot of time trying to find the source of the issue and Stephen´s response helped a lot. Hope this answer provides some more information about the issue.

Solution 4 - C#

> Is my test flawed, or is there some web.config element I'm missing > here that would make HttpContext.Current resolve correctly after an > await?

Your test is not flawed and HttpContext.Current should not be null after the await because in ASP.NET Web API when you await, this will ensure that the code that follows this await is passed the correct HttpContext that was present before the await.

Solution 5 - C#

I wanted to comment and say that the previous answers hit the nail on the head from a web.config perspective, however, there can be inherited settings from IIS that can override this functionality if you simply use <httpRuntime targetFramework="4.5" />.

What I mean: The true fix here is this setting: <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

If you don't explicitly include this setting but rely on <httpRuntime targetFramework="4.5" /> to configure this setting - it will be overridden by any settings in IIS.

If you are debugging or logging the type of SynchronizationContext and you find out it's of type Legacy, you may want to check for a setting at the IIS or hosting Site level.

Will yield LegacyAspNetSynchronizationContext:

Web.config:

    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="aspnet:UseLegacyEncryption" value="true" />
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.8" />
    <httpRuntime targetFramework="4.5" />
  </system.web>

enter image description here

Will yield AspNetSynchronizationContext (this is what you want):

    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
    <add key="aspnet:UseLegacyEncryption" value="true" />
    <add key="aspnet:UseLegacyMachineKeyEncryption" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.8" />
    <httpRuntime targetFramework="4.5" />
  </system.web>

*Note there is no override setting in IIS here

Solution 6 - C#

I tried all the other answers here and HttpContext.Current was still null.

.net Framework 4.6.1 / MVC

I'm using HttpContext.Current to get the mapped path for an upload to App_Data

This is how I fixed the issue:

I just obtain the current HttpContext.Current in a variable and then reset it after my await calls.

var tempHttpContextCurrent = System.Web.HttpContext.Current;

var report = await reportingUtilities.GetReport(reportId, currentUserRubixId).ConfigureAwait(false);

// reset the HttpContext.Current after the await call.
System.Web.HttpContext.Current = tempHttpContextCurrent;

Solution 7 - C#

In my case the problem was I forget await in the begining of stack, explicity in my constructor action method

NOTE: Using net core 5.0 with IHttpContextAccessor

So the problem was in code...

[HttpPost]
public async Task AnyAction()
{
    await service.MethodReturningTask(); //await was not
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionweleganView Question on Stackoverflow
Solution 1 - C#Stephen ClearyView Answer on Stackoverflow
Solution 2 - C#Todd MenierView Answer on Stackoverflow
Solution 3 - C#abarrenecheaView Answer on Stackoverflow
Solution 4 - C#Darin DimitrovView Answer on Stackoverflow
Solution 5 - C#Mark C.View Answer on Stackoverflow
Solution 6 - C#tvb2754View Answer on Stackoverflow
Solution 7 - C#Juan PabloView Answer on Stackoverflow