In aspnet, I can set a "session" log pr request using
using (LogContext.PushProperty("CorrelationId", "SomeUniqeSessionId"));
and then use the Disposal pattern to pop the property off again, so I have a id to track the users session.
In Blazor Serverside, how can I get the same functionality ? I want to have the log message from the same user session correlated, in order to track "ok, what did the user do to get to here and cause this error?"
A F5 refresh on the browser should then give a new session ID (afaicu), since its re-loading the entire page. Off course I can subclass the logger, or store it in a custom session object, but would like to use the "normal" Serilog constructs, where the logger is injected ect ect.
Related
I am trying to use HttpContext.Session in my ASP.NET Core Blazor Server application (as described in this MS Doc, I mean: all correctly set up in startup)
Here is the code part when I try to set a value:
var session = _contextAccessor.HttpContext?.Session;
if (session != null && session.IsAvailable)
{
session.Set(key, data);
await session.CommitAsync();
}
When this code called in Razor component's OnAfterRenderAsync the session.Set throws following exception:
The session cannot be established after the response has started.
I (probably) understand the message, but this renders the Session infrastructure pretty unusable: the application needs to access its state in every phase of the execution...
Question
Should I forget completely the DistributedSession infrastructure, and go for Cookies, or Browser SessionStorage? ...or is there a workaround here still utilizing HttpContext.Session? I would not want to just drop the distributed session infra for a way lower level implementation...
(just for the record: Browser's Session Storage is NOT across tabs, which is a pain)
Blazor is fundamentally incompatible with the concept of traditional server-side sessions, especially in the client-side or WebAssembly hosting model where there is no server-side to begin with. Even in the "server-side" hosting model, though, communication with the server is over websockets. There's only one initial request. Server-side sessions require a cookie which must be sent to the client when the session is established, which means the only point you could do that is on the first load. Afterwards, there's no further requests, and thus no opportunity to establish a session.
The docs give guidance on how to maintain state in a Blazor app. For the closest thing to traditional server-side sessions, you're looking at using the browser's sessionStorage.
Note: I know this answer is a little old, but I use sessions with WebSockets just fine, and I wanted to share my findings.
Answer
I think this Session.Set() error that you're describing is a bug, since Session.Get() works just fine even after the response has started, but Session.Set() doesn't. Regardless, the workaround (or "hack" if you will) includes making a throwaway call to Session.Set() to "prime" the session for future writing. Just find a line of code in your application where you KNOW the response hasn't sent, and insert a throwaway call to Session.Set() there. Then you will be able to make subsequent calls to Session.Set() with no error, including ones after the response has started, inside your OnInitializedAsync() method. You can check if the response is started by checking the property HttpContext.Response.HasStarted.
Try adding this app.Use() snippet into your Startup.cs Configure() method. Try to ensure the line is placed somewhere before app.UseRouting():
...
...
app.UseHttpsRedirection();
app.UseStaticFiles();
//begin Set() hack
app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
//this throwaway session variable will "prime" the Set() method
//to allow it to be called after the response has started
var TempKey = Guid.NewGuid().ToString(); //create a random key
Context.Session.Set(TempKey, Array.Empty<byte>()); //set the throwaway session variable
Context.Session.Remove(TempKey); //remove the throwaway session variable
await Next(); //continue on with the request
});
//end Set() hack
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
...
...
Background Info
The info I can share here is not Blazor specific, but will help you pinpoint what's happening in your setup, as I've come across the same error myself. The error occurs when BOTH of the following criteria are met simultaneously:
Criteria 1. A request is sent to the server with no session cookie, or the included session cookie is invalid/expired.
Criteria 2. The request in Criteria 1 makes a call to Session.Set() after the response has started. In other words, if the property HttpContext.Response.HasStarted is true, and Session.Set() is called, the exception will be thrown.
Important: If Criteria 1 is not met, then calling Session.Set() after the response has started will NOT cause the error.
That is why the error only seems to happen upon first load of a page--it's because often in first loads, there is no session cookie that the server can use (or the one that was provided is invalid or too old), and the server has to spin up a new session data store (I don't know why it has to spin up a new one for Set(), that's why I say I think this is a bug). If the server has to spin up a new session data store, it does so upon the first call to Session.Set(), and new session data stores cannot be spun up after the response has started. On the other hand, if the session cookie provided was a valid one, then no new data store needs to be spun up, and thus you can call Session.Set() anytime you want, including after the response has started.
What you need to do, is make a preliminary call to Session.Set() before the response gets started, so that the session data store gets spun up, and then your call to Session.Set() won't cause the error.
SessionStorege has more space than cookies.
Syncing (two ways!) the sessionStorage is impossible correctly
I think you are thinking that if it is on the browser, how can you access that in C#? Please see some examples. It actually read from the browser and transfers (use) on the server side.
sessionstorage and localstorage in blazor are encrypted. We do not need to do extra for encryption. The same applies for serialization.
I'm facing a very peculiar issue with sessions being reset without any apparent reason. This happens randomly, once every few tens or hundreds of requests.
My web application is running on windows 2003, IIS 6.0, .NET 1.1. The application has a webpage which populates a bunch of Session variables during its Page_Load event. The data is stored out of process in ASPNET State Service.
After the Page_load event exits and the page is displayed, the user clicks on a button, which retrieves the session data and does some work with it.
And this Button_click is where the issue occurs. On some occasions, the session variable is null, raising a nullRefException.
Our traces show that the sessionID during the Button_click event is a brand new session, with a different ID than the session of the Page_Load event. Thus, the application fails to retrieve the data that was stored during Page_Load. Our event log shows that the session variables for the problematic requests are indeed populated during the Page_load event, and the response is sent without issue, which normally would persist the data.
We have ruled out session timeouts; although a timeout would still result in the same nullRefException, the same session ID from Page_load would be used to retrieve non-existing data. In this case, the sessionID is different than the original.
We are not messing with the ASPNET cookie in any way, we do not use session.abandon, nor do we inadvertedly remove items from the session.
My question is: what server-side factors could cause the cardholder's session to be reset like that? The Application event log does not contain any useful info.
Also, is there anything client-side (e.g. cookie tampering) that could force IIS to assign a new session upon subsequent postbacks of the page?
Many thanks in advance.
I'm not sure if this applies to your situation, but it might help others.
I was designing a website and I found out the hard way, meaning I had to redesign a portion of this site I was working on. When you create or delete a folder (from an asp.net page) within the active IIS folder it resets all sessions for the website. This means every user currently on the site gets their sessions instantly deleted.
If you have control of your the server, store files outside the IIS folder and stream them in as needed. If you don't have control of the server, you will have to remove any work with folders.
Let's say I have a client who is filling up a data from a website. the underlying persistence used is Nhibernate.
Now the series of events goes like this
user fills up the form .
he submits the form .
the nhibernate sessionfactory is created and via customer object its is saved to database .
the database commits the session object using a native generator for nhibernate
but a mishap happens before the response from the server reaches the client . the connection to the client goes off .
the client gets to see a page not found error or request time out error . and has a dilemma that he isnt registered yet .
so he again presses the refresh button . and the same set of data (although that is committed to database ) has been send to server for database commit.
the server sees the data and again registers the same customer with different id .
so the problem goes like . now the same customer entry has been duplicated two times cause the connection has got cut off .
Now can someone tell me how to proceed into this scenario. So that even if the customer submits he just recognizes only one entry and submits it, even if the response has got cut off and he presses the refresh button?
Thoughts:
1) You don't want to create the NHibernate SessionFactory on every request. This should be created once and then used by all future requests. It's a heavy-weight operation. Only the Sessions need to be created each request.
2) Manage the transaction in a high level method - to reduce the likelihood of something going wrong AFTER you've committed the transaction, but BEFORE the client has a response.
3) Guard against the "Refresh" method resubmitting, by having the submit function return a different page which presents the information. This page should not submit anything.
4) Guard against a resubmit server side by validating the submission against previously submitted credentials. Inform the client if they've previously registered and provide them with a means to access the saved details (Password recovery for example.)
so for example if your users are keyed by email address then in the page load event perform the following steps:
I want to store values in variables to access form another page (a.k.a State management).
Now I cannot use sessions since I have multiple Zope instances & if one fails the user need to be redirected to another Zope instance and one session is valid only for one Zope instance.
Now my remaining options are
submit a Hidden input tag using POST method
Passing through URL with GET method
Using cookies
Using Database (which I think is 'making simple things complex'.)
I am not even considering the first 2 methods and I think using cookies is not secure.
So is there a commercial or open source module that can securely (encryption etc.) do cookie management.
If not I will have to use a database.
Please inform me, if I am missing something.
Version - Zope 2.11.1
The SESSION support built-in to Zope 2 actually keeps the session in a temporary partition of the ZODB so I think it actually is valid for multiple Zope clients connecting to the same ZEO server. The cost of this is that all session changes invoke the transaction machinery and result in a commit, so just make sure you're not using the SESSION in something very low-level like PAS auth or you'll have commits hitting your ZODB for every image, CSS file, and JS file.
The master page in my web application does authentication and loads up the user entity using a Get.
After this whenever the user object is needed by the usercontrols or any other class I do a Load.
Normally nhibernate is supposed to load the object from cache or return the persistent loaded object whenever Load of called. But this is not the behavior shown by my web application. NHprof always shows the sql whenever Load is called. How do I verify the correct behavior of Load?
I use the S#arp architecture framework.
Actually, calling Load on an entity not marked as lazy causes immediate loading. That is because non-lazy entities are never proxied. In this case, it just acts the same way as Get.
If you use Get, then a hit to a database is made.
If you use Load, no hit to a database is made, but the object (User in your case) is created with 'lazy loading'. So when you check a property it knows that you want data so it hits the database with a query to get the data.
If you want to get an object from cache, you need to consider 2 options.
First level cache, is a cache that is in use in ONE session. So when you close a session or load the same object in a different session, you get additional hits.
Second level cache works accross all sessions. If one session gets the object, the other session gets it from cache.
So what you want is probably a second level cache.