简体   繁体   English

服务器端 .NET Core 中的 Session.SetString() 产生错误“响应开始后无法建立会话”

[英]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谢谢

Answer回答

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");
});
...
...

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.我可以在此处分享的信息不是 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.HasStartedtrue ,并且调用了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.HasStartedtrue ,这就是引发错误的原因。 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.

相关问题 开始响应后无法建立会话 - The session cannot be established after the response has started ASP.NET Core 3.1 中的 Session.SetString 方法抛出异常“IFeatureCollection 已被处理。对象名称:&#39;Collection&#39;。” - Session.SetString method throws exception "IFeatureCollection has been disposed. Object name: 'Collection'. " in ASP.NET Core 3.1 .net会话状态已创建一个会话ID,但无法保存它,因为该响应已被应用程序刷新 - .net Session state has created a session id, but cannot save it because the response was already flushed by the application ASP.Net Core 中间件无法设置异常状态码,因为“响应已经开始” - ASP.Net Core middleware cannot set status code on exception because "response has already started" ASP.Net core JWT Bearer authentication with Keycloak StatusCode 无法设置,因为响应已经开始 - ASP.Net core JWT Bearer authentication with Keycloak StatusCode cannot be set because the response has already started 无法访问会话 asp.net 核心 - Cannot access session asp.net core 会话ASP.NET MVC上的本地存储或服务器端会话? - Session Local Storage or Server side session at ASP.NET MVC? .NET Core 2中的状态服务器会话状态 - State Server Session State in .NET Core 2 在对 IIS 服务器上托管的 .NET CORE 2.1 应用程序进行一次 AJAX 调用后,会话状态被清除或丢失 - Session State being cleared or lost after one AJAX call to .NET CORE 2.1 application hosted on IIS server 会话作为.net核心 - session as reference .net core
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM