Why is HttpContext.Current null after await? - c#

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?

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.

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.

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 <system.web> 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):
Instead of LegazyAspNetSynchronizationContext (Which was what I saw before adding the target framework):
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.
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.

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.

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

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;

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
}

Related

MVC Session Variable working on Localhost but not on live server?

In my MVC5 application, I have a data-grid which has it's filter/sort criteria specified inside the URL. When a user selects a Record to Edit/Create/Delete, I needed to save the URL value so that when the Controller POST action completed, they would be returned to the exact same layout they were on before the GET action was triggered (same data-grid with sort/filter criteria applied).
In my pursuit of this funcitonality, I began using a session variable in my Controller. On GET:
Session["returnURL"] = Request.UrlReferrer.AbsoluteUri;
I set a session variable of returnURL to the current full path URL of my Browser. The on the post, after my changes to the record/database are saved, I have check the returnURL variable for null and perform a Redirect():
var returnURL = (Session["returnURL"] != null) ? Session["returnURL"].ToString() : Url.Action("Index", "Home");
return Redirect(returnURL);
This all works perfectly fine on my localhost, but when I publish it to my Server each time I attempt to navigate to Edit/Create/Delete actions for my record, the Session Variable on the GET actions causes:
Server Error in '/' Application.
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error: [No relevant source lines]
After a lot of trial and error I diagnosed that it is my Session Variable that is causing this NullReferenceException as when I remove that specific code line from my GET action method, everything else functions fine and the appropriate View loads.
Can anyone helps with this? I'm at a loss for why my Session Variable is working on my Localhost but not on my Server. I took a look at the following MSDN article https://msdn.microsoft.com/en-us/library/h6bb9cz9(v=vs.71).aspx, but I'm still getting the same result even with <configuration><system.web> <sessionState mode="InProc"></sessionState>... specified in my Web.Config.
EDIT:
At ps2goat's suggestion, I modified my Web.Config to include:
<system.web>
<sessionState mode="InProc"></sessionState>
// ....
<pages enableSessionState="true">
<namespaces>
<add namespace="GridMvc" />
<add namespace="MvcSiteMapProvider.Web.Html" />
<add namespace="MvcSiteMapProvider.Web.Html.Models" />
</namespaces>
</pages>
// ....
</system.web>
Still receiving the same NullReferenceException error...? I have now specified the sessionState = InProc and set <pages enableSessionState="true">.
I found the answer in this post (The Session object is null in ASP.NET MVC 4 webapplication once deployed to IIS 7 (W 2008 R2)), wherein I modified my <modules> section to do an add/remove of the below specified values:
<configuration>
...
<system.webServer>
...
<modules>
<remove name="Session" />
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
...
</modules>
</system.webServer>
</configuration>
I'm still not entirely sure WHY this is the answer, but it worked for my issue in this case.

Allow Potentially dangerous content in C# [duplicate]

I have a form at which I use ckeditor. This form worked fine at Asp.Net 2.0 and 3.5 but now it doesn't work in Asp.Net 4+. I have ValidateRequest="false" directive. Any suggestions?
Found solution on the error page itself. Just needed to add requestValidationMode="2.0" in web.config
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime requestValidationMode="2.0" />
</system.web>
MSDN information: HttpRuntimeSection.RequestValidationMode Property
There is a way to turn the validation back to 2.0 for one page. Just add the below code to your web.config:
<configuration>
<location path="XX/YY">
<system.web>
<httpRuntime requestValidationMode="2.0" />
</system.web>
</location>
...
the rest of your configuration
...
</configuration>
I know this is an old question, but if you encounter this problem in MVC 3 then you can decorate your ActionMethod with [ValidateInput(false)] and just switch off request validation for a single ActionMethod, which is handy. And you don't need to make any changes to the web.config file, so you can still use the .NET 4 request validation everywhere else.
e.g.
[ValidateInput(false)]
public ActionMethod Edit(int id, string value)
{
// Do your own checking of value since it could contain XSS stuff!
return View();
}
This works without changing the validation mode.
You have to use a System.Web.Helpers.Validation.Unvalidated helper from System.Web.WebPages.dll. It is going to return a UnvalidatedRequestValues object which allows to access the form and QueryString without validation.
For example,
var queryValue = Server.UrlDecode(Request.Unvalidated("MyQueryKey"));
Works for me for MVC3 and .NET 4.
Note that another approach is to keep with the 4.0 validation behaviour, but to define your own class that derives from RequestValidator and set:
<httpRuntime requestValidationType="YourNamespace.YourValidator" />
(where YourNamespace.YourValidator is well, you should be able to guess...)
This way you keep the advantages of 4.0s behaviour (specifically, that the validation happens earlier in the processing), while also allowing the requests you need to let through, through.

Glimpse works locally, not on remote server

After following all the guides, SO pages and troubleshooting pages I can find I'm finally out of ideas.
I've got Glimpse working fine on my local dev server, but when I deploy my ASP.net (MVC5) app to my remote server it doesn't work - at all. /glimpse.axd gives a 404 with both LocalPolicy and ControlCookiePolicy set to ignore, and with a custom security policy that returns On in all cases. My understanding is that with ControlCookiePolicy disabled, I shouldn't need to go to /glimpse.axd to enable it - but I'm not seeing the glimpse icon on the remote server either.
Even if I go to the remote server and browse localhost to /glimpse.axd I still get a 404.
My web.config looks like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="glimpse" type="Glimpse.Core.Configuration.Section, Glimpse.Core" />
</configSections>
<system.web>
<compilation debug="false" />
<httpRuntime targetFramework="4.5.1" relaxedUrlToFileSystemMapping="true" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
</modules>
<urlCompression doDynamicCompression="true" dynamicCompressionBeforeCache="false" />
</system.webServer>
<glimpse defaultRuntimePolicy="On" endpointBaseUri="~/Glimpse.axd">
<logging level="Trace" />
<runtimePolicies>
<ignoredTypes>
<add type="Glimpse.AspNet.Policy.LocalPolicy, Glimpse.AspNet" />
<add type="Glimpse.Core.Policy.ControlCookiePolicy, Glimpse.Core" />
</ignoredTypes>
</runtimePolicies>
</glimpse>
</configuration>
This is the version off the remote server (after transform). I've trimmed it a little to remove sections like appSettings.
My GlimpseSecurityPolicy.cs looks like this:
// Uncomment this class to provide custom runtime policy for Glimpse
using Glimpse.AspNet.Extensions;
using Glimpse.Core.Extensibility;
namespace RationalVote
{
public class GlimpseSecurityPolicy:IRuntimePolicy
{
public RuntimePolicy Execute(IRuntimePolicyContext policyContext)
{
return RuntimePolicy.On;
}
public RuntimeEvent ExecuteOn
{
// The RuntimeEvent.ExecuteResource is only needed in case you create a security policy
// Have a look at http://blog.getglimpse.com/2013/12/09/protect-glimpse-axd-with-your-custom-runtime-policy/ for more details
get { return RuntimeEvent.EndRequest | RuntimeEvent.ExecuteResource; }
}
}
}
The real one does an actual check, but I get the same issue with the policy above.
I cannot seem to find any trace output anywhere on the remote server, it is logging fine on my local machine.
I am deploying using the Visual Studio publish to web feature, and I've verified that the Glimpse.Core.dll is in the bin folder.
I can't see anything in the event log that is relevant.
I've also added <add namespace="Glimpse.Mvc.Html" /> to the namespaces block of the web.config in the views folder.
I tried putting #Html.GlimpseClient() in the _Layout.cshtml file just above </body> but this renders nothing.
Anybody got any ideas?
If the glimpse.axd is returning a 404 then this means the Glimpse resource handler is not registered.
If the web.config content you show above is not trimmed to much, then it is normal that Glimpse won't do much as the Glimpse HttpModule and the Glimpse HttpHandler are not registered in the system.web and/or the system.webserver sections like this
<system.web>
<httpModules>
<add name="Glimpse" type="Glimpse.AspNet.HttpModule, Glimpse.AspNet"/>
</httpModules>
<httpHandlers>
<add path="glimpse.axd" verb="GET" type="Glimpse.AspNet.HttpHandler, Glimpse.AspNet"/>
</httpHandlers>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules>
<add name="Glimpse" type="Glimpse.AspNet.HttpModule, Glimpse.AspNet" preCondition="integratedMode"/>
</modules>
<handlers>
<add name="Glimpse" path="glimpse.axd" verb="GET" type="Glimpse.AspNet.HttpHandler, Glimpse.AspNet" preCondition="integratedMode" />
</handlers>
</system.webServer>
Maybe your transform removed to much from the local web.config?

How to synchronously call DiscoveryClient.Find in ASP.NET

I'm trying to discover a service using DiscoveryClient from an ASP.NET page
void Page_Load(...) {
...
var result = DiscoveryClient.Find(anyCriteria);
....
}
The call fails whith an exception:
System.InvalidOperationException: Asynchronous operations are not allowed in this context
StackTrace:
at System.Web.AspNetSynchronizationContext.OperationStarted()
at System.ServiceModel.Discovery.DiscoveryClient.InitializeAsyncOperation(AsyncOperationContext context)
at System.ServiceModel.Discovery.DiscoveryClient.FindAsyncOperation(FindCriteria criteria, Object userState)
at System.ServiceModel.Discovery.DiscoveryClient.FindAsync(FindCriteria criteria, Object userState)
at System.ServiceModel.Discovery.DiscoveryClient.Find(FindCriteria criteria)
I tried using <%Page ... Async="true" %> without any change.
How do I call DiscoveryClient.Find in an ASP.NET page correctly?
Did you set (web.config)
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
See http://msdn.microsoft.com/en-us/library/hh975440.aspx
"Setting this compatibility switch is mandatory for WebSockets-enabled applications, for using Task-based asynchrony in Web Forms pages, and for certain other asynchronous behaviors."
I'm pretty sure that the latest discovery client uses the TPL.
The AllowAsyncDuringSyncStages option has to be set to 'true'.
<appSettings>
<add key="aspnet:AllowAsyncDuringSyncStages" value="true" />
</appSettings>
This only works with AspNetSynchronizationContext, which is enforced by the UseTaskFriendlySynchronizationContext setting. See Kenneth's answer for details.

Canceling request validation using HttpHandler on IIS 7

I have an application that has to deal with getting "special" characters in its URL (like &, +, %, etc). When I'm sending a request to the application using these characters (of course I'm sending them escaped) I'm getting "Bad Request" response code with the message "ASP.NET detected invalid characters in the URL". Tracing the request shown me that the error was thrown from the "Authentication Module".
I've searched a bit and found that every page has a ValidateRequest and changing its value to false solves the problem. Unfortunately I'm using Httphandler. Does anyone know how to stop the request validation using http handler?
I ran into the same problem (creating an IHttpHandler that needed to receive requests with special characters in the URL). I had to do two things to fix it:
Create the following DWORD registration entry with a value of 1: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\VerificationCompatibility
In the web.config, set the allowDoubleEscaping attribute of the requestFiltering element to true.
I had the same problem and got it working by setting validateRequest="false" as well as requestValidationMode="2.0", like below. No registry edits.
<system.web>
<httpRuntime requestValidationMode="2.0" />
...
<pages ... validateRequest="false" />
</system.web>
Wholesale changes at the application or machine level were not acceptable to me. I only had one parameter that a client was sending incorrectly that I needed to scrub.
I found that when a request is made with html or other potentially dangerous items in the query string, you can scrub the string by using the context.Request.RawUrl and, once the query string is scrubbed, use context.RewritePath(scrubbedUrl).
First thing in the handler, get the context.Request.RawUrl
Scrub the bad request RawUrl for bad input
context.RewritePath(srubbedUrl)
Profit
It seems like request validation only cares about accessing context.Request.Params[], so if you scrub and rewrite path (to the same path) before the Params collection is accessed, you're golden.
Below solution worked for me-
Create the following DWORD registration entry with a value of 1: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\VerificationCompatibility
In the web.config, set the allowDoubleEscaping attribute of the requestFiltering element to true.
You can remove all the modules with
<httpModules>
<clear />
</httpModule>
to make the request get to your handler. Or maybe you can remove the specific modules that are stopping your request.
This is the list of modules loaded by default in ASP.NET 2.0 from here
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />
<add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
<add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
<add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
<add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" />
<add name="Profile" type="System.Web.Profile.ProfileModule" />
</httpModules>
How about this?
<system.web>
<pages validateRequest="false">
</pages>
</system.web>
Since .NET Framework 4.5, you can use the Unvalidated property on HttpRequest or HttpRequestBase to access form, query string and URL data in a way that will not trigger request validation. If you avoid accessing these values in any other way (including in helper methods or other HTTP modules running in the same request), you will not require anything else to avoid request validation.
You can set validateRequest to false in the pages section of the web.config. Maybe that works for HttpHandlers as well?

Categories

Resources