Appearance
Are you an LLM? You can read better optimized documentation at /core_features/session_management.md for this page in Markdown format
Session management
This document provides a detailed technical overview of session management in TheExampleApp. It covers how session state is configured, initialized, and utilized to maintain user-specific data across requests. A clear understanding of this mechanism is crucial for developers maintaining features related to user preferences and runtime configuration.
Session configuration
In TheExampleApp, session state is managed by the standard ASP.NET Core Session middleware. As a server-side rendered application built on ASP.NET Core 2.1, this approach provides a robust mechanism for persisting user-specific data on the server between HTTP requests.
Session state is primarily used for:
- Runtime Configuration Overrides: Storing user-provided Contentful API credentials (
ContentfulOptions) to allow switching the content source without restarting the application. See Runtime Settings for more details. - Localization Preferences: Persisting the user's selected locale, including handling complex fallback scenarios where the desired locale is not fully supported by the application's static resources. See Localization.
- Feature Toggles: Storing flags for enabling or disabling features, such as the editorial features.
It is important to note that while most user-specific state is handled via server-side session, the tracking of visited lessons is managed separately using a client-side cookie.
Session setup
Session management is configured and enabled in the Startup.cs file. The setup involves two key steps: registering the session service in the dependency injection container and adding the session middleware to the application's request pipeline.
Session Middleware Configuration
In the ConfigureServices method, the AddSession extension method is called to register session services. A custom idle timeout is configured for this application.
csharp
// File: TheExampleApp/Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// ... other service configurations
services.AddSession(options => {
// IdleTimeout is set to a high value to conform to requirements for this particular application.
// In your application you should use an IdleTimeout that suits your application needs or stick to the default of 20 minutes.
options.IdleTimeout = TimeSpan.FromDays(2);
});
// ... other service configurations
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Key Points:
- Idle Timeout: The
IdleTimeoutis set to 2 days. This is a non-standard, long duration specified by the application's requirements. The default is 20 minutes. Developers should be aware that this keeps session data on the server for a long time, which has memory implications if the application serves a large number of concurrent users. - Session Store: By default, ASP.NET Core session uses an in-memory store (
DistributedMemoryCache). This means session data will be lost if the application is restarted. For production environments requiring persistence across restarts or load-balanced scenarios, configuring a distributed cache (e.g., Redis, SQL Server) would be necessary.
Middleware Pipeline Integration
In the Configure method, app.UseSession() is called to add the session middleware to the HTTP request pipeline. The placement of this call is critical.
csharp
// File: TheExampleApp/Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// ... other middleware
app.UseStaticFiles();
app.UseSession();
app.UseRequestLocalization(/* ... */);
// ... other middleware
app.UseMvc(/* ... */);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Key Points:
- Order of Middleware:
UseSession()must be called before any middleware that needs to access session data. In this application, it is placed beforeUseRequestLocalizationandUseMvc, as both the localization providers and the Razor Pages/Controllers rely on session state.
Session usage
The application stores several distinct pieces of data in the session. Data is stored as key-value pairs, where values are typically serialized to strings (often JSON).
| Session Key | Stored Data | Description & Location of Logic |
|---|---|---|
ContentfulOptions | JSON string of a ContentfulOptions object. | Stores custom Contentful credentials (Space ID, API keys) entered by the user on the settings page. This allows the app to fetch content from a different Contentful space at runtime. Logic is in ContentfulOptionsManager.cs and Pages/Settings.cshtml.cs. |
locale | String (e.g., "de-DE"). | The user's preferred language and region, set via a query parameter. Used by CustomRequestCultureProvider to determine the culture for the request. Logic is in Startup.cs. |
fallback-locale | String (e.g., "en-US"). | Stores a supported UI culture if the user's preferred locale is not fully supported by the app's static resources but is available in Contentful. This enables a hybrid localization approach. Logic is in Startup.cs. |
EditorialFeatures | String ("Enabled" or "Disabled"). | A flag to toggle editorial features, set on the settings page. Logic is in Pages/Settings.cshtml.cs. |
SettingsErrors | JSON string of validation errors (List<ModelStateError>). | Temporarily stores model state errors during the settings page post/redirect/get flow to repopulate validation messages. Logic is in Pages/Settings.cshtml.cs. |
SettingsErrorsOptions | JSON string of a SelectedOptions object. | Temporarily stores the user-submitted options alongside validation errors during the settings page post/redirect/get flow to repopulate the form. Logic is in Pages/Settings.cshtml.cs. |
Session-based features
The following sections detail the implementation of key features that rely on state management.
VisitedLessonsManager Implementation
Contrary to what might be expected, the tracking of visited lessons does not use server-side session state. Instead, it uses a client-side cookie for persistence. This design choice offloads this specific piece of state to the client, reducing server memory usage. The VisitedLessonsManager class encapsulates this logic.
The manager is registered as a transient service in Startup.cs, making it available for dependency injection throughout the application. For more context, see the Visited Lessons feature documentation.
csharp
// File: TheExampleApp/Configuration/VisitedLessons.cs
public class VisitedLessonsManager: IVisitedLessonsManager
{
private readonly string _key = "ContentfulVisitedLessons";
private readonly IHttpContextAccessor _accessor;
public List<string> VisitedLessons { get; private set; }
public VisitedLessonsManager(IHttpContextAccessor accessor)
{
VisitedLessons = new List<string>();
_accessor = accessor;
// On instantiation, read the cookie from the current request.
var cookie = _accessor.HttpContext.Request.Cookies[_key];
if (!string.IsNullOrEmpty(cookie))
{
VisitedLessons = new List<string>(cookie.Split(';', StringSplitOptions.RemoveEmptyEntries));
}
}
public void AddVisitedLesson(string lessonId)
{
VisitedLessons.Add(lessonId);
var options = new CookieOptions();
options.HttpOnly = true;
options.Expires = DateTime.Now.AddDays(7);
// Append the updated, distinct list of IDs to the response cookie.
_accessor.HttpContext.Response.Cookies.Append(_key, string.Join(';', VisitedLessons.Distinct()), options);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Implementation Details:
- Persistence: Lesson IDs are stored in a single cookie named
ContentfulVisitedLessonsas a semicolon-delimited string. - Reading State: The manager's constructor reads and parses the cookie from the incoming
HttpRequest. - Writing State: The
AddVisitedLessonmethod appends a new lesson ID and writes the updated string back to theHttpResponse, setting a 7-day expiration for the cookie. - Dependency: It relies on
IHttpContextAccessorto access the request and response objects.
Locale Fallback Handling
The application uses a sophisticated CustomRequestCultureProvider that leverages session to manage localization. This provider enables a scenario where content can be served in a locale defined in Contentful, while the application UI (static strings) falls back to a supported culture if the primary one is not available.
This logic is defined inline within Startup.cs.
csharp
// File: TheExampleApp/Startup.cs
// ... inside app.UseRequestLocalization
new CustomRequestCultureProvider(async (s) => {
if (s.Request.Query.ContainsKey(LOCALE_KEY))
{
// ...
var locale = s.Request.Query[LOCALE_KEY];
// ... validation against Contentful locales ...
if(SupportedCultures.Any(c => string.Equals(c.ToString(), locale, StringComparison.OrdinalIgnoreCase)) == false)
{
// The locale is supported in Contentful, but not by the application.
// Walk through the fallback chain to see if we find a supported locale there.
var fallback = GetNextFallbackLocale(/* ... */);
if(fallback != null)
{
// We found a fallback, use that for static strings.
s.Session.SetString(LOCALE_KEY, locale);
s.Session.SetString(FALLBACK_LOCALE_KEY, fallback);
// Use the fallback for static strings
return await Task.FromResult(new ProviderCultureResult(fallback, fallback));
}
}
s.Session.SetString(LOCALE_KEY,locale);
}
return null;
}),1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Execution Flow:
- A request arrives with a query parameter (e.g.,
?locale=es-MX). - The provider checks if
es-MXis in the app'sSupportedCultureslist (e.g.,en-US,de-DE). Let's assume it is not. - It then checks the locale fallback chain defined in Contentful for
es-MX. Ifes-MXfalls back toen-US, anden-USis inSupportedCultures, a match is found. - The provider stores the original selection and the fallback in the session:
Session["locale"] = "es-MX"Session["fallback-locale"] = "en-US"
- It returns
en-USas the culture for the current request's UI. TheContentfulClientwill still usees-MXto fetch content. - On subsequent requests without the query parameter, another
CustomRequestCultureProviderreads these values from the session, ensuring the user's preference is maintained.
For more information on service registration, see Service Configuration.