CHANGELOG for migration details.
DavidHome.RssFeedis the base library that contains builders, processors, discovery contracts and models.DavidHome.RssFeed.Optimizelyadds Optimizely discovery, processors, routing and the scheduled job described below.DavidHome.RssFeed.Storage.AzureBlobpersists generated feeds in Azure Blob Storage.
Install the packages your scenario requires (for Optimizely you typically reference all three).
Add the feed services during startup:
using DavidHome.RssFeed.Contracts;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddDavidHomeRssFeed(Configuration)
.AddOptimizelyFeedIntegration(Configuration)
.AddDefaultOptimizelyProcessors()
// Register each feed container/feed item pair that should expose a feed.
.AddContentPageFeed<ArticleFeedContainerPage, ArticleFeedItemPage>(
Configuration,
new[] { typeof(ArticleFeedContainerPage).Assembly, typeof(ArticleFeedItemProcessor).Assembly })
// Optional storage provider(s).
.AddAzureBlobStorage(Configuration.GetSection("ConnectionStrings:EPiServerAzureBlobs"));
}
public void Configure(IApplicationBuilder app)
{
// Ensures the Blob container exists at startup.
app.UseAzureBlobRssFeed();
}
}Key points:
AddDavidHomeRssFeedwiresIRssFeedBuilderand bindsDavidHome:RssFeedoptions.AddOptimizelyFeedIntegrationregisters discovery, routing and Optimizely helper services.AddDefaultOptimizelyProcessorsadds the provided processors. Call it beforeAddContentPageFeedif you want to override any processor registrations with your own implementations.AddContentPageFeed<TContainer, TItem>(..., params IReadOnlyCollection<Assembly> assembliesToScan)associates a container/item pair and scans assemblies forIRssFeedContainerProcessor/IRssFeedItemProcessorimplementations. Pass every assembly that contains processors you want registered.- Register one or many
IRssFeedStorageProviderimplementations (AddAzureBlobStorageshown above, or a custom provider).
For each pair of content types that should produce a feed:
- Implement the marker interfaces
IRssFeedSourceContainer<TFeedItem>on the container type andIRssFeedSourceItem<TFeedContainer>on the item type. These are the types thatAddContentPageFeedregisters. - Implement
IContentRssFeedso the Optimizely processors can accessIContent. No RSS-specific properties need to be added to the content types in 2.0.0.
[ContentType(DisplayName = "Article Feed Container", GUID = "00000000-0000-0000-0000-000000000001")]
public class ArticleFeedContainerPage : PageData, IRssFeedSourceContainer<ArticlePage>, IContentRssFeed
{
}
[ContentType(DisplayName = "Article", GUID = "00000000-0000-0000-0000-000000000002")]
public class ArticlePage : PageData, IRssFeedSourceItem<ArticleFeedContainerPage>, IContentRssFeed
{
}The default processors (OptimizelyContentContainerProcessor and OptimizelyContentItemProcessor) convert your Optimizely content into the runtime IRssFeedContainer/IRssFeedItem implementations and set all required metadata at runtime, but you can supply additional processors to enrich or override values. If you define a processor with generic parameters that match the feed types (IRssFeedItemProcessor<ArticlePage> for example), register it in one of the assemblies supplied to AddContentPageFeed.
The library reads its settings from DavidHome:RssFeed in your configuration. A single set of defaults applies to every feed, and a child object named after a container type overrides values for that feed only.
{
"DavidHome": {
"RssFeed": {
"SerializeExtensionsAsAtom": false,
"ContentMaxLength": 25000000,
"MaxSyndicationItems": 50,
"ContentAreaPropertyName": "MainContentArea",
"ArticleFeedContainerPage": {
"ContentMaxLength": 15000000,
"ContentAreaPropertyName": "MainContentArea",
"FeedTitlePropertyName": "HeadTitle",
"MaxSyndicationItems": 25
}
}
}
}| Name | Description | Default |
|---|---|---|
| ContentMaxLength | Max number of characters that will be written to the <description> for each item. |
1000000 |
| MaxSyndicationItems | Maximum number of items emitted per feed. | 50 |
| SerializeExtensionsAsAtom | When true, extensions are serialized using the Atom namespace. |
false |
Optimizely-specific overrides (placed either at root or under the container type name):
| Name | Description | Default |
|---|---|---|
| FeedRelativeUrl | Relative segment appended to the container page URL (supports multiple levels such as "rss/latest"). |
"rss" |
| ContentAreaPropertyName | Content area used to generate item body HTML. Leave null to skip body generation. | null |
| FeedTitlePropertyName | Fallback property name for the feed title. Defaults to Name. |
null |
When using DavidHome.RssFeed.Storage.AzureBlob, provide the connection string section to AddAzureBlobStorage. For Optimizely DXP you can re-use the built-in connection string:
var blobConnection = Configuration.GetSection("ConnectionStrings").GetSection("EPiServerAzureBlobs");
services.AddDavidHomeRssFeed(Configuration)
// ...
.AddAzureBlobStorage(blobConnection);Also ensure the project references a recent Microsoft.Extensions.Azure package (the helper does not work with older versions).
Call IRssFeedBuilder.BuildFeeds() to generate feeds for every registered container. It returns an IAsyncEnumerable<SyndicationFeedResult?>, so you must await foreach the sequence and expect null entries whenever processors detect invalid data.
The Optimizely package exposes a ready-to-use scheduled job (RssFeedGeneratorScheduledJob). Once the assembly is referenced, enable the job from the Optimizely admin UI to run feed generation on an interval. The job simply iterates the feeds and calls every registered IRssFeedStorageProvider:
IRssFeedStorageProvider implementations are multi-language and host aware starting in 2.0.0, so be sure to pass the Language and HostNameIdentifier values everywhere you call Save or GetSavedStream. The Azure Blob provider stores blobs under <host>/<language>/<feedId>.xml.
- Partial routing for each container is automatically registered when calling
AddContentPageFeed. Visiting/path/to/container/<FeedRelativeUrl>returns the feed. - The Optimizely package already includes
RssFeedDataController, so once the NuGet is referenced and the site is rebuilt you automatically get the controller that streams the generated XML from every registeredIRssFeedStorageProvider. - To add the RSS
<link>element to the HTML<head>of a container page, call the provided HTML helper:@Html.SyndicationLink()(fromDavidHome.RssFeed.Optimizely.Contracts.Extensions). It renders markup such as<link href="https://www.example.com/rss/" rel="alternate" title="My Articles" type="application/rss+xml">. - Because discovery now runs per enabled language and per site host, ensure each feed container has published content for the languages/hosts you expect. That guarantees the builder returns one
SyndicationFeedResultper(container, host, language)combination and that the partial router can resolveFeedRelativeUrlcorrectly.