Appearance
Are you an LLM? You can read better optimized documentation at /features/preview_mode.md for this page in Markdown format
Preview mode
This document provides a technical overview of the draft content preview functionality within TheExampleApp. This feature allows users, typically content editors, to view unpublished or draft content from the Contentful CMS directly within the application, rendered as it would appear on the live site.
Draft content preview
The core of this feature is the ability to switch the application's data source from the standard Contentful Delivery API to the Contentful Preview API. This switch happens at runtime and is managed on a per-user session basis, allowing for seamless toggling between live and draft content without requiring an application restart or affecting other users.
This functionality is essential for a robust editorial workflow, enabling content creators to verify layout, formatting, and interactive elements before publishing their changes to the public.
Delivery vs Preview API
Contentful provides two distinct APIs for content retrieval, and TheExampleApp is architected to use both. The choice of API dictates the version of the content that is fetched and displayed.
Delivery API (CDA): This is the primary, public-facing API.
- Content: Serves only published content entries.
- Performance: It is highly optimized for speed and availability, leveraging a global CDN for caching.
- Configuration: Uses the
DeliveryApiKeyspecified in the application's configuration.
Preview API (CPA): This is a non-performant API intended for internal preview purposes.
- Content: Serves all content, including draft, changed, and published entries. This allows editors to see the latest version of their work.
- Performance: It is not cached via CDN and reflects changes in near real-time. It is not intended for production traffic.
- Configuration: Uses the
PreviewApiKeyspecified in the application's configuration.
The base configuration for these APIs is defined in appsettings.json. The UsePreviewApi flag determines the default API for the entire application upon startup.
json:TheExampleApp/appsettings.json
{
// ... other settings
"ContentfulOptions": {
"DeliveryApiKey": "df2a18b8a5b4426741408fc95fa4331c7388d502318c44a5b22b167c3c1b1d03",
"PreviewApiKey": "10145c6d864960fdca694014ae5e7bdaa7de514a1b5d7fd8bd24027f90c49bbc",
"SpaceId": "qz0n5cdakyl9",
"UsePreviewApi": false // Default setting for the application
}
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
For more details on the base API setup, see the API Configuration documentation.
Switching APIs
While the default API is set at startup, the application provides a mechanism for users to switch between the Delivery and Preview APIs at runtime. This is a session-specific setting, ensuring that one user entering preview mode does not affect another user's browsing experience.
Runtime API Selection
The API switching logic is initiated from a user interface element, which posts to the OnPostSwitchApi handler in Settings.cshtml.cs.
This handler performs the following actions:
- Receives the target API (
cdafor Delivery orcpafor Preview) and the current page path. - Clones the existing
ContentfulOptionsfrom the current context. - Sets the
UsePreviewApiproperty based on the user's selection. - Serializes the new
ContentfulOptionsobject into a JSON string. - Stores this string in the user's
Sessionunder the keyContentfulOptions. - Redirects the user back to their original page.
csharp:TheExampleApp/Pages/Settings.cshtml.cs
/// <summary>
/// Post action from the dropdown to switch api.
/// </summary>
/// <param name="api">The api to switch to.</param>
/// <param name="prevPage">The page that originated the post.</param>
/// <returns>A redirect result back to the originating page.</returns>
public IActionResult OnPostSwitchApi(string api, string prevPage)
{
// Retain all options except whether to use the preview api or not.
var options = new ContentfulOptions
{
UsePreviewApi = api == "cpa", // Set UsePreviewApi based on selection
DeliveryApiKey = _manager.Options.DeliveryApiKey,
SpaceId = _manager.Options.SpaceId,
PreviewApiKey = _manager.Options.PreviewApiKey,
// ... other options are retained
};
// Serialize the new options and store them in the user's session.
HttpContext.Session.SetString(nameof(ContentfulOptions), JsonConvert.SerializeObject(options));
return Redirect($"{prevPage}?api={api}");
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Applying Session Configuration
To make the session-based setting effective, the application uses a custom ContentfulOptionsManager to dynamically provide the correct ContentfulOptions when the IContentfulClient is resolved.
Note: The ContentfulOptionsManager class is normally not needed in typical applications. It is only present in TheExampleApp to allow switching credentials at runtime for demonstration purposes. Most applications will use static configuration from appsettings.json and won't require this pattern.
The ContentfulOptionsManager is registered as a singleton and its Options property contains the core logic:
- It attempts to retrieve the
ContentfulOptionsJSON string from the current user's session. - If session data exists, it deserializes it and returns the resulting
ContentfulOptionsobject. - If no session data is found, it returns the default
_optionsthat were loaded fromappsettings.jsonat startup.
csharp:TheExampleApp/Configuration/ContentfulOptionsManager.cs
/// <summary>
/// Gets the currently configured <see cref="ContentfulOptions"/> either from session, if present, or from the application configuration.
/// </summary>
public ContentfulOptions Options {
get {
// Check the session for a custom configuration.
var sessionString = _accessor.HttpContext.Session.GetString(nameof(ContentfulOptions));
if (!string.IsNullOrEmpty(sessionString))
{
// If found, deserialize and return it.
return JsonConvert.DeserializeObject<ContentfulOptions>(sessionString);
}
// Otherwise, fall back to the application's default options.
return _options;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
This pattern allows for flexible, runtime configuration overrides. For more information, see the Runtime Settings documentation.
Contentful Client Instantiation
The final piece is ensuring the IContentfulClient is created using these dynamic options. This is achieved by registering IContentfulClient with a transient lifetime and a custom factory in Startup.cs.
For each request, the dependency injection container will:
- Resolve the
IContentfulOptionsManager. - Call its
Optionsproperty, which returns either the session-specific or default options. - Instantiate a new
ContentfulClientusing these options.
csharp:TheExampleApp/Startup.cs
// This would normally not be needed, but since we want to load our ContentfulOptions from memory if they're changed within the application
// we provide our own implementation logic for the IContentfulClient
services.AddSingleton<IContentfulOptionsManager, ContentfulOptionsManager>();
services.AddTransient<IContentfulClient, ContentfulClient>((ip) => {
var client = ip.GetService<HttpClient>();
// Get the options manager and retrieve the current, potentially session-specific, options.
var options = ip.GetService<IContentfulOptionsManager>().Options;
var contentfulClient = new ContentfulClient(client,
options);
// ... additional client configuration
return contentfulClient;
});1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
UI Indicator: SelectedApiViewComponent
To provide clear feedback to the user, the SelectedApiViewComponent is used to display the currently active API. It injects the IContentfulClient and checks its IsPreviewClient boolean property. This property is automatically set by the contentful.aspnetcore SDK based on the UsePreviewApi option used during its creation.
csharp:TheExampleApp/ViewComponents/SelectedApiViewComponent.cs
/// <summary>
/// View component for the API selector dropdown.
/// </summary>
public class SelectedApiViewComponent : ViewComponent
{
private readonly IContentfulClient _client;
public SelectedApiViewComponent(IContentfulClient client)
{
_client = client;
}
/// <summary>
/// Invokes the view component and returns the result.
/// </summary>
/// <param name="currentPath">The path of the current request. Used to redirect the request back after changing the api.</param>
/// <returns></returns>
public IViewComponentResult Invoke(string currentPath)
{
return View(Tuple.Create(_client.IsPreviewClient, currentPath));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
This component is a good example of how UI elements can react to the application's dynamic state. For more on this pattern, see the View Components documentation.
Use cases
The preview mode architecture supports several key development and editorial activities:
- Content Preview Before Publishing: This is the primary use case. An editor can create or modify content in Contentful, save it as a draft, and then activate preview mode in
TheExampleAppto see their changes in the context of the full website. - Editorial Workflow: The feature facilitates a review process. An editor can share a preview link (by appending the appropriate query parameters) with a stakeholder or reviewer, allowing them to see and approve content before it is made public. See the Editorial Features documentation for more on this workflow.
- Testing Content Changes: Developers and QA testers can use preview mode to test how new or modified Contentful content models render in the application. This allows for validation of structural changes without affecting the live site or requiring a separate testing environment with duplicated content.