ASP.NET 核心 2.1 会话



在 ASP.NET 核心 2.1 中,我无法访问会话变量。

调试时,我注意到在每个请求中会话 ID 都会更改 (HttpContex.Session.Id)

我在会话配置中是否出错?

启动.cs

public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Adds a default in-memory implementation of IDistributedCache.
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(1000);
options.Cookie.HttpOnly = true;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSession();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}

程序.cs

public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}

调试时,我注意到在每个请求会话ID中都会更改 (HttpContex.Session.Id)

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ucms6.Models;
namespace ucms6.Controllers
{
public class HomeController : Controller
{
const string SessionKeyName = "_Name";
const string SessionKeyYearsMember = "_YearsMember";
const string SessionKeyDate = "_Date";
public IActionResult Index()
{
// Requires using Microsoft.AspNetCore.Http;
HttpContext.Session.SetString(SessionKeyName, "Rick");
HttpContext.Session.SetInt32(SessionKeyYearsMember, 3);
return RedirectToAction("SessionNameYears");
//  return View();
}
public IActionResult SessionNameYears()
{
var name = HttpContext.Session.GetString(SessionKeyName);
var yearsMember = HttpContext.Session.GetInt32(SessionKeyYearsMember);
return Content($"Name: "{name}",  Membership years: "{yearsMember}"");
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}

Startup类的ConfigureServices()方法中,按如下方式设置options.CheckConsentNeeded = context => false;

services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false; // Default is true, make it false
options.MinimumSameSitePolicy = SameSiteMode.None;
});

解决方案是将会话 cookie 标记为必需。

public void ConfigureServices(IServiceCollection services)
{
//...
services.AddSession(opt =>
{
opt.Cookie.IsEssential = true;
});
//...
}

有关船旗的文件指出:

指示此 Cookie 对于应用程序正常运行是否必不可少。如果为 true,则可以绕过同意策略检查。默认值为 false。

这将使 Cookie 策略选项保持不变,并且会话仍按预期工作CookiePolicyOptions.CheckConsentNeeded因为仅影响非必要的 Cookie。

Core 中的默认分布式缓存存储 ASP.NET 内存中。由于会话使用分布式缓存,这意味着会话存储也在内存中。存储在内存中的东西是进程绑定的,因此如果进程终止,存储在内存中的所有内容都会随之终止。最后,当您停止调试时,应用程序进程将终止。这意味着每次启动和停止调试时,您都有一个全新的会话存储。

您可以采取几条路线。首先,如果只想运行站点而不对其进行调试,则可以使用CTRL+F5。这将启动 IIS Express 并加载您的 Web 应用程序,而无需启动所有调试机制。然后,您可以继续发出任意数量的请求,并且所有请求都将命中相同的进程(这意味着您的会话存储将保持不变)。这对于执行开发的前端非常有用,因为您可以修改Razor视图,CSS,JS等并查看这些更改,而无需停止并再次开始调试。但是,如果对 C# 代码进行任何更改(类、控制器等),Visual Studio 将启动生成,这将终止应用程序,然后重新启动它。您的网站继续运行,就好像什么都没发生一样,但存储在内存中的任何内容(包括您的会话)都将消失。不过,这至少比不断调试要好。

其次,您也可以在开发中使用持久存储(您应该已经设置为在生产中使用持久存储,因此如果没有,请尽快修复该存储)。您可以在开发中使用 SQL Server 或 Redis 之类的东西,就像在生产中使用一样。可以将 SQL 存储添加到现有的开发数据库中,因此实际上不需要安装 SQL Server。您还可以安装 Redis 的本地副本,如果您更喜欢该路由,只需从本地主机运行它。无论使用哪种方法,您的分布式缓存以及您的会话都将存储在应用程序外部的东西中,因此启动和停止应用程序不会影响存储在那里的内容。

我花了一个小时试图解决这个问题,所以为了以防万一,发布了另一个解决方案。

确保增加默认超时,即 10 秒。当用户填写需要大量时间的长表单时 - 如果花费的时间超过 10 秒,它可能会自行重置。

services.AddSession(options =>
{
options.Cookie.IsEssential = true;
options.IdleTimeout = TimeSpan.FromMinutes(10);
});

"默认情况下"我的意思是每个使用会话的 .NET 程序员都可能从此页面复制粘贴代码,并且设置为 10 秒。无论如何,我就是这样做的。;)

最新更新