Take the community feedback survey now.
Take the community feedback survey now.
 
                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.