This is a WCF Oneway operation. HttpContext.Current.User is cleared in those operations, that is the reason I added a behavior that saves the User before it is cleared. Later I want to re-set the HttpContext.Current.User with the value I saved but I'm getting an exception:
HttpContext.Current.User = (RolePrincipal)userThatWasSavedBefore;
Object reference not set to an instance of an object.
at System.Web.HttpContext.SetPrincipalNoDemand(IPrincipal principal, Boolean needToSetNativePrincipal)
at System.Web.HttpContext.set_User(IPrincipal value)
at (My Function)
Why can't I set the user? What is the problem?
The reason you can't set it is most likely that the request has finished. Setting the User property results in ASP.NET trying to call back into per-request data structures, and if these data structures have been released then the resulting behavior is undefined.
For one-way operations, WCF invokes application code while simultaneously telling ASP.NET not to wait for the application code to finish and to just complete the request immediately. For this reason is it strongly recommended that you don't access HttpContext from a one-way operation.
Clearly some object in the line throwing the exception is null.
Check whether you have a current HttpContext, that is, I suspect HttpContext.Current is null.
The call stack shows an instance method of HttpContext is executed.
In other words - HttpContext is there, the exception is provoked by something else - a missing NulRef check likely for _notificationContext.
The NotificationContext is internal and unset only by OnRequestNotificationCompletionHelper as I write.
Exception scenario seem to look like:
Some work is to be done async - does not block/prevent a request from being completed
Request processing is finished
Your worker item sets HttpContext.User for the finished request
This looks like a framework bug :(
This problem is either because some object in these classes is null (such as HttpContext.Current)
OR
The user which you are passing there (and you saved before) is actually pointing to null. As you can see from stack trace, this is very likely the case, because the exception is thrown by System.Web.HttpContext.set_User(IPrincipal value)
User is actually not a variable, but a property. And changing it to another value is calling a function, that throws this exception in case new value for user is null.
In order to find out what is causing this problem I recommend you to set a breakpoint to the line which is throwing the exception and check if any part of that line isn't pointing to null
crash proof alternative could be:
if (HttpContext.Current != null && userThatWasSavedBefore != null)
{
HttpContext.Current.User = (RolePrincipal)userThatWasSavedBefore;
}
Related
I'm running into a very odd issue where the refresh token "disappears" after a few hours on an Azure App Service which hosts my Wep Api project. I've implemented OAuth for my password flow. Our AccessToken expires after 1 hour and our RefreshToken expires after one week.
For some background. This is happening on an Azure App service where i'm hosting my Web Api and a mobile front end is making calls to it (there are more than one users/mobile devices making a call to this app service).
Here's what a sample initial call looks like using /token call:
My grant_type is password. Normally i get back a refresh_token field along with the access_token, token_type and expires_in.
Works fine for the first few hours after i push to the app service then refresh_token disappears. I am truly stumped by this issue.
Here's my CreateAsync Code:
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
{
return;
}
string refreshTokenId = await CreateRefreshTokenId(clientid, context);
if (refreshTokenId != null)
{
context.SetToken(refreshTokenId);
}
else
{
throw new Exception("refresh token could not be created");
}
}
private async Task<string> CreateRefreshTokenId(string clientId, AuthenticationTokenCreateContext context)
{
var ticket = context.Ticket;
var refreshTokenId = Guid.NewGuid().ToString("n");
var refreshTokenLifeTime = ConfigurationManager.AppSettings["as:clientRefreshTokenLifeTime"];
var token = new CreateRefreshTokenDTO
{
RefreshTokenId = refreshTokenId,
ClientId = clientId,
Subject = ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
ticket.Properties.IssuedUtc = token.IssuedUtc;
ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await createRefreshTokenManager.ManagerRequest(new CreateRefreshTokenRequest
{
RefreshToken = token
});
return result.IsError ? null : refreshTokenId;
}
I've added the exception in the else statement to see if it will throw and exception and it does in fact throw, which leads me to believe that the refreshTokenId is null. I've also added logging to a log table but for whatever reason, when this error is thrown it should save to the DB table (which i've tested locally and works) but on the App server it is not saving to the table. Very perplexing... UPDATE: PLEASE SEE UPDATE BELOW ON WHY NO LOGS WERE SAVING
Then, what is supposed to happen after this is that now that the front end (mobile, in this case) has the access and refresh tokens, when the access token expires, another call is made to /token but with grant_type = refresh_token:
UPDATE
Eventually I was able to reproduce the issue locally through trial and error and waiting for access token to expire (not entirely sure). But in any case, I was able to produce this error:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
This error was the reason i was not able to save any logs to the DB.
Im using Castle Windsor as my IoC and EF6.
My calls are in this order:
1] Attempt to validate the context. In here i make another await call to a LoginUserManager where I basically get and verify user info
// This is used for validating the context
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
2] CreateAsync for creating access and refresh tokens from Context
public async Task CreateAsync(AuthenticationTokenCreateContext context)
Inside CreateAsync I make an await call CreateOrUpdateRefreshTokenManagerwhich either does an Update if entry exists or a Create. And ultimately make a SaveChanges(). This SaveChanges() is what causes the error If I don't call a SaveChanges() no entry is updated or created in that table. This is odd because in other parts of my code i dont call SaveChanges() at all at the end of the web request lifecycle yet an update/create/delete is made. Im assuming that EF/Windsor handles the saving for me.
My thoughts is that because this flow is different from all my other endpoints and that its handling two Async calls that somewhere in between I am disposing the DbContext and that is maybe why im seeing it failing on the second (CreateAsync) call. Not sure, just my thought here.
Anyway, sorry for the long winded post here. I wanted to post as much info as possible and am hoping that this may also help someone else facing this or similar issue.
Thanks!
UPDATE 2
I've noticed that after getting this error on /token call, any other (AllowAnonymous) calls i make work - even those that involve the DB. But the /token call in particular no longer works. My only way around this is to restart the server.
UPDATE 3
I was able to reproduce this issu ONLY on mobile testing (linked to Azure server) but cannot reproduce locally. Steps I used to reproduce:
Log in with one account
Logout
Log in with another account
Logout
Log in with the first account I tried) - This FAILS
Alright ya'll I was able to figure out this issue and i'll do my best to describe what was happening here.
For those of you who have followed a tutorial such as this one or any other similar one, you'll see that you basically have some repository structure set up along with maybe your own context which you inherit the base context, right?
In my case, I was handling the Dispose of the context at the end of the request by overriding the Dispose(bool disposing) method found in the ApiController class. If you're building a WebApi, you'll know what im talking about because any controllers you write inherit this. And if you've got some IoC set up with Lifetimes set to the end of a request, it'll dispose there :)
However, in this case of the /token call I noticed that we were never hitting any controllers...or at least none that utilized ApiController so i couldn't even hit that Dispose method. That meant that the context stayed "active". And in my second, third, fourth, etc calls to /token endpoint, I noticed in the watcher that the context calls were building up (meaning it was saving prior edits to the context i made from previous /token calls - they were never getting disposed! Thus making the context stale).
Unfortunately, the way around this for my situation was to wrap any context calls made within the /token request in a using statement, that way i knew after the using finished up it would dispose properly. And this seemed to work :)
So if you've got a similar project laid out to mine or similar to that tutorial i shared you may run into this issue.
When I run the project, I get this line highlighted with an error:
string cartID = context.Request.Cookies["Cinemax_CartID"].Value;
The message is as follows:
Object reference not set to an instance of an object.
Thanks for any suggestions!
The problem is that you have to check if the cookie exists, if it does, then you can read its value in a safe way, otherwise you should initialize the cookie value or you would get a null reference exception.
This is actually the same pattern you should apply when reading items from the ViewState, Session, Application, Cookies, etc. basically you cannot rely on an external value, you should check if it actually exists
Try something like this:
if(context.Request.Cookies["Cinemax_CartID"] == null)
{
// initialize the cookie
context.Request.Cookies["Cinemax_CartID"].Value = initial_value;
}
myCookieValue = context.Request.Cookies["Cinemax_CartID"].Value;
I don't know, why do we use HttpContext.Current?
In this property I use it for Session but I don't know why!
public static string Name
{
get
{
if (HttpContext.Current.Session["_n_"] != null)
return HttpContext.Current.Session["_n_"].ToString();
else return "";
}
set
{
HttpContext.Current.Session["_n_"] = value;
}
}
HttpContext is an object that wraps all http related information into one place. HttpContext.Current is a context that has been created during the active request. Here is the list of some data that you can obtain from it.
Request type (Post, Get)
Request parameters (querystring, posted data)
User's IP address
Cookies
Further you can control your output through this object. In Items property, which is a dictionary, you can store instances of objects to ensure that they are created once for the request. You can control the output stream applying your custom filters.
This is a short list of that what you can do with this property.
It's a way to get access to the current HttpContext someplace that may not have a reference to the context but is within an active web request.
That's like saying "Why do I need to go to a bank to get money?", to which the answer is "Because that's where the money is.
To answer your question. Because that's where the Session is. It's really that simple. You don't have to know why, just that that's where it is.
There's a much longer explanation, which other people are giving with all the technical details. But in the end, the answer just boils down to this.
before asp.net MVC in a web form, there were classes request, response where you can get cookies and session and those staff in MVC all the HTTP information like request and response and their properties are now inside HTTpcontext.
i have an application wherein i have incorporate a "Remember Me" feature for the login screen.
I do this by creating a cookie when the user logs in for the first time, so next time when the user visits the site i get the cookie and load the user information.
i have written the code for loading user information in a common class in the App_Code folder...and all my pages inherit from this class.
code for loading the user info is as follows:
public static void LoadUserDetails(string emailId)
{
UsersEnt currentUser = UsersBL.LoadUserInfo(emailId);
if (currentUser != null)
HttpContext.Current.Session["CurrentUser"] = currentUser;
}
Now the problem is i get an "Object reference" error when i try to store the currentUser object in the session variable (even though the currentUser object is not null). However the password property in the currentUser object is null.
Am i getting the error because of this...or is there some other reason??
thank you
Assuming it's the final line which is causing the problem, that suggests that either HttpContext.Current or HttpContext.Current.Session is null. I suggest you find out which it is, and then work out why.
HttpContext.Current.Session is probably null.
Code that uses the State has to be placed after the AcquireRequestState Event has called. See the page lifecycle for more information.
Try putting your code after or inside the Page_Load method.
If it is throwing the exception on the line:
HttpContext.Current.Session["CurrentUser"] = currentUser;
Then the only other explanation is that HttpContext.Current.Session is null.
Unable to cast object of type 'System.Web.Caching.CachedRawResponse' to type 'System.Web.Caching.CachedVary'.
I'm getting this on an AJAX call to an aspx page, and can find no information about it in webland. CachedRawResponse isn't even on MSDN. Does anyone know anything about it, or maybe point me in the right direction?
We recently had the same problem, and it turned out (in our case) that the page output cache module is rather sensitive to how you set your Response.Cache.VaryByXyz properties. We used code like the following in our HTTP compression module:
if (IsBrowserSupported(userAgent))
{
Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
...
}
Unfortunately, this causes ASP.NET to throw a fit when the page is cached after a call by a non-supported browser, and subsequently requested from the cache by a supported browser.
Not setting any VaryByXyz causes a CachedRawResponse to be stored in the ASP.NET output cache, but if you do set any VaryByXyz during your request, ASP.NET expects a CachedVary at that location. And instead of checking whether the cached page is of the right type, the framework just casts, resulting in an InvalidCastException.
Moral of the story: always set the VaryByXyz consistently, regardless of the request headers or other non-request-related variables. In our case, placing the VaryByHeaders outside of the if solved the error.