Opticon Stockholm is on Tuesday September 10th, hope to see you there!
Opticon Stockholm is on Tuesday September 10th, hope to see you there!
Im guessing you need to implement IPartialRouter : Partial routing (optimizely.com)
You do need a mechnism to find the page we used Search and Navigation e.g. below
public class CaseStudyRouter : IPartialRouter<FolderPage, CaseStudyPage>
{
#if DEBUG
private readonly TimeSpan _cacheTimespan = new TimeSpan(0, 0, 0);
#else
private readonly TimeSpan _cacheTimespan = new TimeSpan(0, 30, 0);
#endif
private ContentReference _container;
private string _urlSegment;
public CaseStudyRouter()
{
var settings = ServiceLocator.Current.GetInstance<ISiteSettingsProvider>();
_container = settings.Current.CaseStudyContainer;
}
public object RoutePartial(FolderPage content, SegmentContext segmentContext)
{
if (!content.ContentLink.CompareToIgnoreWorkID(_container))
{
return null;
}
var nextSegment = segmentContext.GetNextValue(segmentContext.RemainingPath);
var urlSegment = nextSegment.Next;
if (string.IsNullOrEmpty(urlSegment))
{
return null;
}
ArticleBasePage article = null;
var cacheKey = $"routing-casestudy-{urlSegment}";
var cache = ServiceLocator.Current.GetInstance<ICache<ArticleBasePage>>();
_urlSegment = urlSegment;
if (cache.IsExists(cacheKey))
{
article = cache.Get(cacheKey, GetArticleBasePage);
}
else
{
article = GetArticleBasePage();
if(article != null) cache.AddToCache(cacheKey, article, _cacheTimespan);
}
if (article != null)
{
segmentContext.RemainingPath = nextSegment.Remaining;
segmentContext.RoutedContentLink = article.ContentLink;
}
return article;
}
private ArticleBasePage GetArticleBasePage()
{
var urlSegment = _urlSegment;
ArticleBasePage article = null;
var searchContext = SearchClient.Instance
.Search<ArticleBasePage>(SearchClient.Instance.Settings.Languages.GetSupportedLanguage("en"))
.Filter(x => x.URLSegment.MatchCaseInsensitive(urlSegment))
.Filter(d => d.Ancestors().Match(_container.ID.ToString()))
.StaticallyCacheFor(this._cacheTimespan)
.Select(r => r.ContentLink);
var searchResults = searchContext.GetResult().FirstOrDefault();
if (!ContentReference.IsNullOrEmpty(searchResults))
{
var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
article = contentRepository.Get<ArticleBasePage>(searchResults);
}
return article;
}
public PartialRouteData GetPartialVirtualPath(
CaseStudyPage content,
string language,
RouteValueDictionary routeValues,
RequestContext requestContext)
{
var contentLink = requestContext.GetRouteValue("node", routeValues)
as ContentReference;
if (!content.ContentLink.CompareToIgnoreWorkID(contentLink))
{
return null;
}
if (PageEditing.PageIsInEditMode)
{
return null;
}
return new PartialRouteData
{
BasePathRoot = _container,
PartialVirtualPath = content.URLSegment
};
}
}
And then Register in an InitialzationModule
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class RegisterArticleDateRouter : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
var caseStudyRouter = new CaseStudyRouter();
RouteTable.Routes.RegisterPartialRouter(caseStudyRouter);
Last snippet will not work in CMS12. You have to register partial router in IoC for `IPartialRouter` interface.
Thank you for your answers and help, Minesh, and Valdis.
For that specific problem, I used two partial routers:
IPartialRouter<FolderPage, PageData>
IPartialRouter<StartPage, FolderPage>
Both use the same GetPartialVirtualPath method implementation:
public PartialRouteData GetPartialVirtualPath(FolderPage content, UrlGeneratorContext urlGeneratorContext)
{
return new PartialRouteData
{
BasePathRoot = ContentReference.RootPage.ToReferenceWithoutVersion(),
PartialVirtualPath = content.URLSegment
};
}
And, as for the RoutePartial logic, I'm using Search (as suggested by Minesh):
var supportedLanguage = _find.Settings.Languages.GetSupportedLanguage(language);
var context = _find.Search<SitePageData>(supportedLanguage)
.Filter(x => x.Language.Name.MatchCaseInsensitive(language))
.Filter(x => x.URLSegment.MatchCaseInsensitive(urlSegmentOfPage))
.Take(1)
.StaticallyCacheFor(_cacheTimeSpan);
var contentResult = context.GetContentResult();
var page = contentResult.FirstOrDefault();
Also, in the RoutePartial logic, I'm setting segmentContext properties:
segmentContext.RemainingSegments = segmentContext.GetNextSegment().Remaining;
segmentContext.Content = page;
All works ok.
Question - is the above solution proper?
I would be a bit wary of using Search and Navigation as part of your routing, even with caching applied. First of all, you're tying the uptime of the site to the uptime of S&N. If S&N hits an issue or you have to clear and rebuild the index, that part of the site will be inaccessible. The other issue would be that, S&N has a rate limit so, when lots of different URLs on your site are being hit in quick succession (for example, when being crawled by a search engine), you risk hitting that rate limit at which point certain URLs will appear to be unavailable. If those URLs appear periodically unavailable to a search crawler, it'll have an adverse affect on your ranking.
Depending on the amount of content which would be surfaced through the partial router, you could maintain the URL -> content reference mapping in memory (perhaps built as part of the site initialisation) though you'd need to manage invalidation/updating of that data when any of the URLs changes.
Thank you @Paul for notice about S&N. I rewrote logic, so it uses a Content Loader with DDS cache.
@Quan: I use simple address to route some of the Commerce data, but the URL generation is done in IPartialRouters.
The case is as following:
Given the actual CMS URL - https://website.com/category/article,
I would like to have a possibility to hide the category segment (and all other segments if there are any) and access the "article" only via https://website.com/article URL.
How can I do that?
I've seen documentation at the https://docs.developers.optimizely.com/content-cloud/v12.0.0-content-cloud/docs/routing but not sure which one of the method would be suitable for my need.
Using CMS.12.15.1 and .NET 6.