[英]Session.SetString() in Server Side .NET Core Produces error "The session cannot be established after the response has started"
I have a server size Blazor app that needs to write something into a session when page is first loaded, and then read it from a session in that page and other pages.我有一个服务器大小的 Blazor 应用程序,它需要在首次加载页面时将某些内容写入会话,然后从该页面和其他页面中的会话中读取它。
Right now, that Session.String() is called in OnInitializedAsync() .现在, Session.String() 在 OnInitializedAsync() 中被调用。 However, I'm getting an exception "The session cannot be established after the response has started".
但是,我收到一个异常“响应开始后无法建立会话”。 From scant documentation that I found, this usually happens when SignalR is used with the app.
根据我发现的少量文档,这通常发生在 SignalR 与应用程序一起使用时。
1) I don't think I'm using SignalR, unless it's configured by default to be used in server-side .net core code (In which case, how do I find out?) 2) I also tried putting the call in OnInitialized() and onAfterRender() (synchronous methods), which didn't help. 1)我不认为我在使用 SignalR,除非它默认配置为在服务器端 .net 核心代码中使用(在这种情况下,我该如何找到?) 2)我也尝试过调用OnInitialized() 和 onAfterRender() (同步方法),这没有帮助。 3) I think my HTTPContextAccessor and ISession are configured correctly, because I'm able to use Session.GetString() anytime, including right before the call to Session.SetString().
3) 我认为我的 HTTPContextAccessor 和 ISession 配置正确,因为我可以随时使用 Session.GetString(),包括在调用 Session.SetString() 之前。 4) I cannot switch to a client-size Blazor app for a variety of reasons.
4) 由于各种原因,我无法切换到客户端大小的 Blazor 应用程序。 5) I'm using app.UseEndpoints(), so app.useMvc() is commented out because they cannot be used at the same time.
5)我正在使用app.UseEndpoints(),所以app.useMvc()被注释掉了,因为它们不能同时使用。
Does anyone have any idea off the top of their head what could be wrong before I paste very large chunks of code here?在我在这里粘贴非常大的代码块之前,有没有人有任何想法可能出了什么问题? The snippets of what I have so far are below
到目前为止我所拥有的片段如下
//Startup.cs
public IHttpContextAccessor HtppContextAccessor { get; }
// ...
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHttpContextAccessor();
//services.AddMvc(); //TODO: DO I NEED IT?
services.AddDistributedMemoryCache(); //TODO : DO I NEED IT? // Adds a default in-memory implementation of IDistributedCache
services.AddSession();
//services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSession();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
//**************************************************************************
//myfile.razor
protected override async Task OnInitializedAsync()
{
var sampleValue = Session.GetString("testName1"); //this call is ok
Session.SetString("testName1", "testValue2"); //this is where exception occurs
}
Thank you谢谢
I think this Session.SetString()
error is a bug, since Session.GetString()
works just fine even after the response has started, but Session.SetString()
doesn't.我认为这个
Session.SetString()
错误是一个错误,因为Session.GetString()
即使在响应开始后也能正常工作,但Session.SetString()
不能。 Regardless, the workaround (or "hack" if you will) includes making a throwaway call to Session.SetString()
to "prime" the session for future writing.无论如何,解决方法(或“hack”,如果你愿意的话)包括对
Session.SetString()
进行一次性调用以“准备”会话以供将来编写。 Just find a line of code in your application where you KNOW the response hasn't sent, and insert a throwaway call to Session.SetString()
there.只需在您的应用程序中找到您知道尚未发送响应的代码行,然后在其中插入对
Session.SetString()
的一次性调用。 Then you will be able to make subsequent calls to Session.SetString()
with no error, including ones after the response has started, inside your OnInitializedAsync()
method.然后,您将能够在
OnInitializedAsync()
方法中毫无错误地对Session.SetString()
进行后续调用,包括响应开始后的调用。 You can check if the response is started by checking the property HttpContext.Response.HasStarted
.您可以通过检查属性
HttpContext.Response.HasStarted
来检查响应是否已启动。
Try adding this app.Use()
snippet into your Startup.cs Configure()
method:尝试将此
app.Use()
片段添加到您的 Startup.cs Configure()
方法中:
...
...
app.UseHttpsRedirection();
app.UseStaticFiles();
//begin SetString() hack
app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
//this throwaway session variable will "prime" the SetString() 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 SetString() hack
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
...
...
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.我可以在此处分享的信息不是 Blazor 特定的,但会帮助您查明设置中发生的情况,因为我自己也遇到过同样的错误。
From scant documentation that I found, this usually happens when SignalR is used with the app.
根据我发现的少量文档,这通常发生在 SignalR 与应用程序一起使用时。
I know you said "usually happens" but just to clarify, this error can still occur without SignalR being installed;我知道您说“通常会发生”,但只是为了澄清一下,在没有安装 SignalR 的情况下仍然会发生此错误; it is not exclusive to SignalR.
它不是 SignalR 独有的。 The reason SignalR is mentioned with this error, is because SignalR uses WebSockets.
此错误中提到 SignalR 的原因是 SignalR 使用 WebSockets。 A WebSocket can easily trigger the error since WebSocket logic most often occurs within a WebSocket send/receive (ping/pong) loop, where the "response" is perpetually being "sent".
WebSocket 很容易触发错误,因为 WebSocket 逻辑最常发生在 WebSocket 发送/接收(ping/pong)循环中,其中“响应”永远被“发送”。 Also, doesn't Blazor use WebSockets as well?
另外,Blazor 不也使用 WebSockets 吗?
Anyway, 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.标准 1.向服务器发送的请求没有会话 cookie,或者包含的会话 cookie 无效/过期。
Criteria 2. The request in Criteria 1 makes a call to Session.SetString()
after the response has started.标准 2。标准 1 中的请求在响应开始后调用
Session.SetString()
。 In other words, if the property HttpContext.Response.HasStarted
is true , and Session.SetString()
is called, the exception will be thrown.换句话说,如果属性
HttpContext.Response.HasStarted
为true ,并且调用了Session.SetString()
,则会抛出异常。
Important: If Criteria 1 is not met, then calling Session.SetString()
after the response has started will NOT cause the error.重要提示:如果不满足条件 1,则在响应开始后调用
Session.SetString()
不会导致错误。
...needs to write something into a session when page is first loaded...
...首次加载页面时需要在会话中写入一些内容...
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 SetString(), that's why I say I think this is a bug).这就是为什么错误似乎只发生在第一次加载页面时 - 这是因为通常在第一次加载时,没有服务器可以使用的会话 cookie(或者提供的会话 cookie 无效或太旧),并且服务器必须启动一个新的会话数据存储(我不知道为什么它必须为 SetString() 启动一个新的,这就是为什么我说我认为这是一个错误)。 If the server has to spin up a new session data store, it does so upon the first call to
Session.SetString()
, and new session data stores cannot be spun up after the response has started.如果服务器必须启动新的会话数据存储,它会在第一次调用
Session.SetString()
时这样做,并且在响应开始后无法启动新的会话数据存储。 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.SetString()
anytime you want, including after the response has started.另一方面,如果提供的会话 cookie 是有效的,则不需要启动新的数据存储,因此您可以随时调用
Session.SetString()
,包括在响应开始之后。
I'm willing to bet that in your case, by the time OnInitializedAsync()
is called, the property HttpContext.Response.HasStarted
is true , which is why the error is being thrown.我敢打赌,在您的情况下,到调用
OnInitializedAsync()
时,属性HttpContext.Response.HasStarted
为true ,这就是引发错误的原因。 What you need to do, is make a preliminary call to Session.SetString()
before the response gets started, so that the session data store gets spun up, and then your call to Session.SetString()
inside the OnInitializedAsync()
method won't cause the error.您需要做的是在响应开始之前对
Session.SetString()
进行初步调用,以便会话数据存储启动,然后在OnInitializedAsync()
方法中对Session.SetString()
的调用获胜'不会导致错误。
That is probably because Asp.Net Core will not write any cookies (including the session cookie) to the response unless the end-user gives their consent according to EU General Data Protection Regulation (GDPR) which is supported in ASP.NET Core and defaults to get the consent from the user.这可能是因为 Asp.Net Core 不会将任何 cookie(包括会话 cookie)写入响应,除非最终用户根据 ASP.NET Core 和默认值支持的欧盟通用数据保护条例 (GDPR) 给予同意征得用户同意。
https://docs.microsoft.com/en-us/aspnet/core/security/gdpr?view=aspnetcore-2.2 https://docs.microsoft.com/en-us/aspnet/core/security/gdpr?view=aspnetcore-2.2
You can try to set the Session Cookie as essential for your application to overcome this:您可以尝试将 Session Cookie 设置为您的应用程序所必需的,以克服此问题:
(From the documentation:) (来自文档:)
Session state cookies are not essential.会话状态 cookie 不是必需的。 Session state isn't functional when tracking is disabled.
禁用跟踪时会话状态不起作用。 The following code makes session cookies essential:
以下代码使会话 cookie 必不可少:
services.AddSession(options =>
{
options.Cookie.IsEssential = true;
});
Better yet, you can follow the guidance in the same article to have user consent feature.更好的是,您可以按照同一篇文章中的指导来获得用户同意功能。
The other question you asked;你问的另一个问题;
services.AddDistributedMemoryCache(); //TODO : DO I NEED IT? // Adds a default in-memory implementation of IDistributedCache
services.AddSession();
Yes;是的; You need to set a Distributed cache, which is documented here:
您需要设置一个分布式缓存,此处记录了该缓存:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1
To enable the session middleware, Startup must contain:要启用会话中间件,Startup 必须包含:
Any of the IDistributedCache memory caches.任何 IDistributedCache 内存缓存。 The IDistributedCache implementation is used as a backing store for session.
IDistributedCache 实现用作会话的后备存储。 For more information, see Distributed caching in ASP.NET Core.
有关详细信息,请参阅 ASP.NET Core 中的分布式缓存。
A call to AddSession in ConfigureServices.在 ConfigureServices 中调用 AddSession。
A call to UseSession in Configure.在 Configure 中调用 UseSession。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.