15

In ASP.NET core 2.1, I cannot access session variables.

While debugging I have noticed that in every request the session ID changes (HttpContex.Session.Id)

Did I make mistake in session configuration?

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

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

While debugging i have noticed that in every request session id changes (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();
    }
ugur
  • 151
  • 1
  • 1
  • 3

4 Answers4

23

In the ConfigureServices() method of the Startup class, Set options.CheckConsentNeeded = context => false; as follows:

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;
        });
TanvirArjel
  • 30,049
  • 14
  • 78
  • 114
  • Thanks it works but why is the default code true? what for? – Suyon Won May 31 '18 at 13:09
  • 6
    My guess for why `CheckConsentNeeded` is `true` by default is for GDPR compliance. If you clicked accept on the consent popup at the top of the page (in the case of the default template), your session cookies would start working as you expected. The GDPR regulates how cookie operate, and will not be used until the user consents to them being used. – Jim Gilmartin Jul 24 '18 at 06:38
11

The solution is to mark the session cookie as essential.

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

The documentation about the flag states:

Indicates if this cookie is essential for the application to function correctly. If true then consent policy checks may be bypassed. The default value is false.

This will keep the cookie policy options intact and the session is still working as expected because CookiePolicyOptions.CheckConsentNeeded only affects non-essential cookies.

SeriousM
  • 3,374
  • 28
  • 33
4

The default distributed cache store in ASP.NET Core is in-memory. Since sessions use distributed cache, that means your session store is also in-memory. Things stored in memory are process-bound, so if the process terminates, everything stored in memory goes along with it. Finally, when you stop debugging, the application process is terminated. That then means every time you start and stop debugging, you have an entirely new session store.

There's a couple of routes you can take. First, if you just want to run the site, without debugging it, you can use CTRL+F5. This will kick off IIS Express and load your web app, without starting all the debugging machinery along with it. You can then proceed to make as many requests as you like, and it will all hit the same process (meaning your session store will be intact). This is great for doing the frontend side of development, as you can modify your Razor views, CSS, JS, etc. and see those changes without have to stop and start debugging again. However, if you make any C# code changes (class, controller, etc.), Visual Studio will kick off a build, which will terminate the application and then restart it. Your site keeps running as if nothing happened, but anything stored in-memory, including your sessions will be gone. It's at least better than debugging constantly, though.

Second, you can simply use a persistent store in development, as well (you should already be setup to use a persistent store in production, so fix that ASAP, if not). You can use something like SQL Server or Redis in development, just like you would in production. The SQL store can be added to your existing development database, so you don't actually need to install SQL Server. You can also install a local copy of Redis and just run it off of localhost, if you prefer that route. With either approach, your distributed cache, and your sessions along with it, will be stored in something external to the application, so starting and stopping your application will have no effect on what's stored there.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • 1
    Thank you for the response. The code above was taken from Microsft docs (except ConfigureServices and Configure in startup.cs). So it should work seamlessly but it is still not working. What I have noticed was that even in a single request , session ID is changed between controller actions. In homecontroller above, session ID changes after redirecting to action SessionNameYears . Do you have any idea? – ugur Mar 17 '18 at 12:33
0

I spent an hour trying to solve this, so posting another solution just in case.

Make sure you increase the default timeout which is 10 seconds. When a user is filling a long form that takes a lot of time - it might reset itself if it takes longer than 10 seconds.

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

And by "default" I mean that every .NET programmer who uses session probably copy-pastes code from this page and it's set 10 seconds. That's what I did anyway. ;)

Alex from Jitbit
  • 53,710
  • 19
  • 160
  • 149