Tutorials ASP.NET Core MVC Mastery
Cookies & Sessions
On this page
State Management: Session & Cookies in ASP.NET Core
HTTP is a stateless protocol. It has amnesia — a server does not remember you from request 1 to request 2. To build applications with shopping carts, multi-step wizards, and user preferences, we must manage "State". Understanding the technical difference between Cookies and Session State is crucial for application architecture and security.
1. Cookies vs Session: The Absolute Truth
Cookies (Client-Side State)
Data is stored entirely on the user's browser as text strings. Sent back to the server with every HTTP request.
- Storage: User's Browser
- Capacity: Extremely limited (~4KB total per domain)
- Security: Visible to user, spoofable (unless encrypted/HttpOnly)
- Use Case: Preferences (Dark mode), anti-forgery tokens, tracking identifiers.
Session (Server-Side State)
Data is stored on the server's memory or database. The server sends a single, tiny encrypted Cookie to the browser containing just a "SessionID".
- Storage: Server Memory (or Redis/SQL Server)
- Capacity: Theoretically unlimited
- Security: Highly secure — data never leaves the server
- Use Case: Shopping carts, multi-step forms, sensitive user context.
2. Implementing Cookies
Cookies do not require any middleware setup. You access them directly via the HttpContext.
Setting and Getting Cookies
public class PreferencesController : Controller
{
public IActionResult SetTheme()
{
// 1. Create secure options
var options = new CookieOptions
{
Expires = DateTimeOffset.UtcNow.AddYears(1), // Persistent cookie
HttpOnly = false, // True blocks JS access. False allows JS (like UI theme switchers)
Secure = true, // Only transmit over HTTPS
SameSite = SameSiteMode.Strict // Prevents CSRF leak
};
// 2. Append to response
Response.Cookies.Append("UserTheme", "Dark", options);
return Content("Theme Saved");
}
public IActionResult ReadTheme()
{
// 3. Read from incoming request
string theme = Request.Cookies["UserTheme"] ?? "Light";
return Content($"Current theme is {theme}");
}
public IActionResult DeleteTheme()
{
// 4. Browsers delete cookies when you set an expired date
Response.Cookies.Delete("UserTheme");
return Content("Cookie deleted");
}
}
3. Implementing Session State
Session requires configuring the server to allocate memory and tracking identifiers.
Step 1: Configuration in Program.cs
// 1. Add Distributed Memory Cache (Session backing store)
builder.Services.AddDistributedMemoryCache();
// 2. Add Session Services
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(20); // Absolute expiration upon inactivity
options.Cookie.HttpOnly = true; // Highly secure, no JS access
options.Cookie.IsEssential = true; // Bypasses GDPR consent for essential auth flows
options.Cookie.Name = ".MyApp.Session";
});
var app = builder.Build();
// 3. IMPORTANT: UseSession MUST be between UseRouting and UseEndpoints/MapControllers
app.UseRouting();
app.UseSession();
app.UseAuthorization();
Step 2: Basic Session Usage
public class ShoppingCartController : Controller
{
public IActionResult AddItem()
{
// ISession only supports Int32 and String natively
HttpContext.Session.SetInt32("CartCount", 1);
HttpContext.Session.SetString("LastItem", "Laptop");
return Ok();
}
public IActionResult ViewCart()
{
int count = HttpContext.Session.GetInt32("CartCount") ?? 0;
string item = HttpContext.Session.GetString("LastItem");
// Remove item
HttpContext.Session.Remove("LastItem");
// Destroy entire session
HttpContext.Session.Clear();
return Ok();
}
}
Step 3: Storing Complex Objects in Session (Production Standard)
Because ISession only accepts strings, you must serialize complex C# objects (like a full Cart) into JSON strings, and deserialize them upon retrieval.
// Extensions/SessionExtensions.cs
using System.Text.Json;
using Microsoft.AspNetCore.Http;
public static class SessionExtensions
{
public static void SetObjectAsJson<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T GetObjectFromJson<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default : JsonSerializer.Deserialize<T>(value);
}
}
// Controller Usage
public IActionResult Checkout()
{
var myCart = new ShoppingCart { Items = 3, Total = 450.50m };
// Save Complex Object
HttpContext.Session.SetObjectAsJson("ActiveCart", myCart);
// Retrieve Complex Object
var retrievedCart = HttpContext.Session.GetObjectFromJson<ShoppingCart>("ActiveCart");
return Ok();
}
4. Production Architecture: Distributed Caching
Web Farm Warning: In-Memory Session Limitations
builder.Services.AddDistributedMemoryCache() stores session data in the Local RAM of the web server. If your app is balanced across 3 servers (Server A, B, C), a user might save their cart on Server A. If their next click routes to Server B, their cart is utterly empty.
In enterprise production, you MUST use a distributed cache like Redis or SQL Server so all web servers access the identical session state.
// Program.cs — Enterprise Redis Registration
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("RedisUrl");
options.InstanceName = "MyAppSession_";
});
builder.Services.AddSession(); // Automatically uses the registered Redis cache!
5. Interview Mastery
Q: "If you store sensitive user preferences, would you use Session or Cookies?"
Architect Answer: "If the data is genuinely sensitive (like an authentication token, financial context, or PII), I strictly use Session State backed by Redis. Cookies live as plain text files on the client's physical hardware, vulnerable to cross-site scripting (XSS), physical machine compromise, or network sniffing if TLS drops. If I must store identifiers in cookies, they are encrypted, digitally signed by ASP.NET Core Data Protection, and marked strictly with HttpOnly and Secure flags to prevent JavaScript access."