ASP.NET Core 2.1会话

15
在ASP.NET Core 2.1中,我无法访问会话变量。在调试过程中,我注意到每个请求中的会话ID都会更改(HttpContex.Session.Id)。我在会话配置方面犯了错误吗? Startup.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();
    }
4个回答

23

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

谢谢,它可以工作,但默认代码为什么是true?有什么用? - Suyon Won
6
我猜测CheckConsentNeeded默认为true是为了符合GDPR合规要求。如果您在同意弹出窗口上点击接受(在默认模板的情况下),则会启动您期望的会话cookie。 GDPR规定了cookie的操作方式,只有在用户同意使用它们之后才会被使用。 - Jim Gilmartin

11

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

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

有关标记状态的文档:

指示此Cookie是否对应用程序的正确功能至关重要。 如果为true,则可能会绕过同意策略检查。 默认值为false。

这将保持Cookie策略选项完好无损,并且会话仍按预期工作,因为CookiePolicyOptions.CheckConsentNeeded只影响非必要的Cookie。


那么这个应用程序仍然符合GDPR的要求吗? - Csharpest
@Csharpest 是的,只要您不在未经同意的情况下跟踪用户并且不保存任何个性化数据,拥有一个会话是没有问题的。 - SeriousM

4
ASP.NET Core的默认分布式缓存存储方式是内存。由于会话使用分布式缓存,这意味着您的会话存储也在内存中。存储在内存中的东西与进程绑定,因此如果进程终止,存储在内存中的所有内容也随之消失。最后,当您停止调试时,应用程序进程也会终止。这意味着每次开始和停止调试时,您都将拥有一个全新的会话存储。
有几种解决方法。首先,如果您只想运行网站而不进行调试,则可以使用CTRL+F5。 这将启动IIS Express并加载您的Web应用程序,而不会同时启动所有调试机制。然后,您可以继续发出尽可能多的请求,它们将全部命中同一进程(这意味着您的会话存储将保持完好)。这非常适合进行前端开发,因为您可以修改Razor视图、CSS、JS等,并查看这些更改,而无需再次停止和启动调试。但是,如果您进行了任何C#代码更改(类、控制器等),Visual Studio将启动构建过程,这将终止应用程序,然后重新启动它。您的网站将继续运行,就像什么也没发生过一样,但是存储在内存中的所有内容,包括您的会话,都将消失。尽管如此,这至少比不停地进行调试要好。
其次,在开发中,您也可以使用持久存储(在生产中应该已经设置了持久存储,如果没有,请尽快修复)。您可以像在生产环境中一样,在开发中使用SQL Server或Redis等内容。 SQL存储可以添加到现有的开发数据库中,因此您实际上不需要安装SQL Server。您还可以安装本地副本的Redis,并在localhost上运行它,如果您喜欢这条路的话。任何一种方法,您的分布式缓存以及与之相关的会话都将存储在应用程序外部的某个地方,因此启动和停止应用程序对其中存储的内容没有影响。

1
感谢您的回复。上面的代码是从微软文档中获取的(除了startup.cs中的ConfigureServices和Configure)。所以它应该可以无缝运行,但实际上还没有。我注意到即使在单个请求中,在控制器操作之间会更改会话ID。在上面的HomeController中,重定向到SessionNameYears操作后,会话ID会更改。您有任何想法吗? - ugur

0

我花了一个小时来解决这个问题,所以在这里发布另一种解决方案。

确保您增加默认超时时间,即10秒。当用户填写需要很长时间的长表单时,如果超过10秒,它可能会重置。

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

而“默认”是指每个使用会话的.NET程序员可能都会从this page复制并粘贴代码,然后将其设置为10秒。无论如何,这就是我所做的。;)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接