<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Reimond's Blog - Episerver Developer</title><link href="http://world.optimizely.com" /><updated>2017-11-14T10:03:12.0000000Z</updated><id>https://world.optimizely.com/blogs/reimonds-blog---episerver-developer/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Episerver CMS and Commerce custom routing</title><link href="https://world.optimizely.com/blogs/reimonds-blog---episerver-developer/dates/2017/11/episerver-cms-and-commerce-custom-routing/" /><id>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h2&gt;&lt;strong&gt;Description&lt;br /&gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We needed to implement and Episerver Website that could be customized for multiple stakeholders. Each stakeholder has his own Personal Website URL, containing a &lt;strong&gt;Site Name&lt;/strong&gt; segment. Because the number of the stakeholders was more than 10000, in order to reuse the Episerver content and do not create personalized content for each stakeholder, we decided to customize Episerver routing for both CMS and Commerce content.&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;h2&gt;&lt;strong&gt;Example&lt;br /&gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Consider that you have the next page tree in Episerver CMS for your stakeholders&amp;rsquo; pages:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/65de3a558a4b4c6dad35c727b592043f.aspx&quot; width=&quot;292&quot; alt=&quot;Image EditCmsTree.png&quot; height=&quot;349&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Please see the next table for exemplifying how Episerver does the default routing and how our custom routing needs to work. The same page should be retrieved for each stakeholder, but with that stakeholder segment in the URL.&lt;/p&gt;
&lt;table border=&quot;1&quot; cellspacing=&quot;10&quot; cellpadding=&quot;10&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Page Name&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;Episerver Default URL&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;Custom Routing URLs&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Stakeholder Home&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder-home/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder1/&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder2/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Become A Stakeholder&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder-home/become-a-stakeholder/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder1/become-a-stakeholder/&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder2/become-a-stakeholder/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;About Me&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder-home/about-me/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/ stakeholder1/about-me/&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/ stakeholder2/about-me/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Shop Now&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder-home/shop-now/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/ stakeholder1/shop-now/&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/ stakeholder2/shop-now/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Shopping Cart&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder-home/shopping-cart/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/ stakeholder1/shopping-cart/&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/ stakeholder2/shopping-cart/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Checkout&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder-home/checkout/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/ stakeholder1/checkout/&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/ stakeholder2/checkout/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For Commerce, the routing is different, because we need to insert the stakeholder segment instead of replacing an existing one, as it is for CMS. Please see below some example of routing for several Catalog items URLs:&lt;/p&gt;
&lt;table border=&quot;1&quot; cellspacing=&quot;10&quot; cellpadding=&quot;10&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Catalog Content Type&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;Episerver Default URL&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;Custom Routing URLs&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Category&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/fashion/mens/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder1/fashion/mens/&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder2/fashion/mens/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Subcategory&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/fashion/mens/mens-shoes/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder1/fashion/mens/mens-shoes/ &lt;br /&gt; &lt;br /&gt;http://quicksilver.localtest.me/en/stakeholder2/fashion/mens/mens-shoes/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;Product&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/fashion/mens/mens-shoes/p-36127195/&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;http://quicksilver.localtest.me/en/stakeholder1/fashion/mens/mens-shoes/p-36127195/ &lt;br /&gt; &lt;br /&gt; http://quicksilver.localtest.me/en/stakeholder2/fashion/mens/mens-shoes/p-36127195/&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;h2&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this section, I will describe the approach that we choose and implementation details for both CMS and Commerce content custom routing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CMS Content custom routing&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
&lt;p&gt;In order to customize default Episerver content routing we need to implement a custom partial router, which implements the public interface &lt;strong&gt;IPartialRouter&amp;lt;TContent, TRoutedData&amp;gt;&lt;/strong&gt;. Please see the next references for details about the &lt;strong&gt;IPartialRouter&lt;/strong&gt; interface and its methods: &lt;a href=&quot;/link/55fe410f4f65482cab72fb59d47c7561.aspx?documentId=cms/10/AB5E122C&quot;&gt;IPartialRouter Interface&lt;/a&gt;, &lt;a href=&quot;/link/55fe410f4f65482cab72fb59d47c7561.aspx?documentId=cms/10/5487072F&quot;&gt;RoutePartial Method&lt;/a&gt; and &lt;a href=&quot;/link/55fe410f4f65482cab72fb59d47c7561.aspx?documentId=cms/10/27574734&quot;&gt;GetPartialVirtualPath Method&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;1.	/*  This custom routing code will be called just for the pages  
2.	    which are under StartPage into the CMS page tree  
3.	    and have page type StakeholderBasePage*/  
4.	public class CustomCmsContentPartialRouter : IPartialRouter&amp;lt;StartPage, StakeholderBasePage&amp;gt;  
5.	    {  
6.	        private readonly IContentLoader _contentLoader = ServiceLocator.Current.GetInstance&amp;lt;IContentLoader&amp;gt;();  
7.	  
8.	        public object RoutePartial(StartPage content, SegmentContext segmentContext)  
9.	        {  
10.	            string stakeholderSiteName;  
11.	            // All the stakeholder pages will inherite from the same base page  
12.	            StakeholderBasePage stakeholderPage = null;  
13.	            var contentUrl = CustomRoutingHelper.GetStakeholderContentUrl(segmentContext, out stakeholderSiteName);  
14.	            var contentReference = CustomRoutingHelper.TryGetContentRefrence(contentUrl);  
15.	  
16.	            if (contentReference != null)  
17.	            {  
18.	                if (CustomRoutingHelper.CheckIfCustomSiteName(segmentContext, stakeholderSiteName))  
19.	                {  
20.	                    stakeholderPage = _contentLoader.Get&amp;lt;StakeholderBasePage&amp;gt;(contentReference.ContentLink);  
21.	  
22.	                    if (stakeholderPage != null)  
23.	                    {  
24.	                        segmentContext.RemainingPath = string.Empty;  
25.	                        segmentContext.RoutedContentLink = stakeholderPage.ContentLink;  
26.	                    }  
27.	                }  
28.	            }  
29.	  
30.	            return stakeholderPage;  
31.	        }  
32.	  
33.	        public PartialRouteData GetPartialVirtualPath(JewelerBasePage content, string language, RouteValueDictionary routeValues,  
34.	            RequestContext requestContext)  
35.	        {  
36.	            string urlSegment;  
37.	            string stakeholderSiteName;  
38.	              
39.	            if (CustomRoutingHelper.CheckIfCustomSiteName(requestContext, out stakeholderSiteName))  
40.	            {  
41.	                urlSegment = GetRoutedContentLink(content, stakeholderSiteName);  
42.	            }  
43.	            else  
44.	            {  
45.	                return null;  
46.	            }  
47.	  
48.	            return new PartialRouteData  
49.	            {  
50.	                BasePathRoot = ContentReference.StartPage,  
51.	                PartialVirtualPath = urlSegment  
52.	            };  
53.	        }  
54.	  
55.	        private string GetRoutedContentLink(StakeholderBasePage content, string stakeholderSiteName)  
56.	        {  
57.	            /*  Stop iteration if content type is StakeholderStartPage. 
58.	                All pages with type StakeholderBasePage should be under  
59.	                StakeholderStartPage into CMS page tree */  
60.	            if (content is StakeholderStartPage)  
61.	                return stakeholderSiteName;  
62.	  
63.	            var parentContent = _contentLoader.Get&amp;lt;StakeholderBasePage&amp;gt;(content.ParentLink);  
64.	  
65.	            return GetRoutedContentLink(parentContent, stakeholderSiteName) + &quot;/&quot; + content.URLSegment;  
66.	        }  
67.	    }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commerce/Catalog Content custom routing&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
&lt;p&gt;In order to customize default Episerver content routing we need to implement a custom partial router, which extends the public class &lt;strong&gt;HierarchicalCatalogPartialRouter&lt;/strong&gt;. Please see the next references for details about the &lt;strong&gt;HierarchicalCatalogPartialRouter&lt;/strong&gt; class and&amp;nbsp; the overrode methods: &lt;a href=&quot;/link/55fe410f4f65482cab72fb59d47c7561.aspx?documentId=commerce/11/E3CA1E12&quot;&gt;HierarchicalCatalogPartialRouter Class&lt;/a&gt;, &lt;a href=&quot;/link/55fe410f4f65482cab72fb59d47c7561.aspx?documentId=commerce/11/80D54BA0&quot;&gt;FindNextContentInSegmentPair Method&lt;/a&gt; and &lt;a href=&quot;/link/55fe410f4f65482cab72fb59d47c7561.aspx?documentId=commerce/11/D25979E4&quot;&gt;GetPartialVirtualPath Method&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;1.	public class CustomCommerceCatalogRouter : HierarchicalCatalogPartialRouter  
2.	{  
3.	    public CustomCommerceCatalogRouter(CatalogContentBase commerceRoot, Func&amp;lt;ContentReference&amp;gt; func)  
4.	    : base(func, commerceRoot, false) { }  
5.	  
6.	    protected override CatalogContentBase FindNextContentInSegmentPair(CatalogContentBase catalogContent, SegmentPair segmentPair, SegmentContext segmentContext, CultureInfo cultureInfo)  
7.	    {  
8.	  
9.	        if (!CustomRoutingHelper.CheckIfCatalogContentUrl(segmentContext.RequestUrl.ToString()))  
10.	            return null;  
11.	  
12.	        CustomRoutingHelper.SkipCustomSiteNameSegment(segmentPair, segmentContext.RequestUrl.ToString());  
13.	        var currentCatalog = base.FindNextContentInSegmentPair(catalogContent, segmentPair, segmentContext, cultureInfo);  
14.	  
15.	        return currentCatalog;  
16.	    }  
17.	  
18.	    public override PartialRouteData GetPartialVirtualPath(CatalogContentBase content, string language, RouteValueDictionary routeValues,  
19.	        RequestContext requestContext)  
20.	    {  
21.	        string stakeholderSiteName;  
22.	  
23.	        var partialRouteData = base.GetPartialVirtualPath(content, language, routeValues, requestContext);  
24.	  
25.	        if (CustomRoutingHelper.CheckIfCustomSiteName(requestContext, out stakeholderSiteName))  
26.	        {  
27.	            partialRouteData.PartialVirtualPath = stakeholderSiteName + &quot;/&quot; + partialRouteData.PartialVirtualPath;  
28.	        }  
29.	  
30.	        return partialRouteData;  
31.	    }  
32.	}  
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Common methods from Helpers&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;CustomRoutingHelper.GetStakeholderContentUrl &lt;/strong&gt;method will return the Episerver Content URL. This method will replace stakeholder site name with stakeholder Episerver home page URL, in return in the stakeholderSiteName output parameter the stakeholder custom site name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E.g.&lt;/strong&gt;: For &lt;a href=&quot;http://website/stakeholder-sitename&quot;&gt;http://website/stakeholder-sitename&lt;/a&gt; the method will return &lt;a href=&quot;http://website/stakeholder-home&quot;&gt;http://website/stakeholder-home&lt;/a&gt; and stakeholderSiteName = &quot;stakeholder-sitename&quot;.&lt;/p&gt;
Please see below the &lt;strong&gt;CustomRoutingHelper&lt;/strong&gt; class:&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;1.	public class CustomRoutingHelper  
2.	{  
3.	    public static string GetStakeholderContentUrl(SegmentContext segmentContext, out string stakeholderSiteName)  
4.	    {  
5.	        var nextSegment = segmentContext.GetNextValue(segmentContext.RemainingPath);  
6.	  
7.	        if (CultureHelper.IsAvailableCulture(nextSegment.Next))  
8.	        {  
9.	            nextSegment = segmentContext.GetNextValue(nextSegment.Remaining);  
10.	        }  
11.	  
12.	        stakeholderSiteName = nextSegment.Next;  
13.	        var regex = new Regex(Regex.Escape(nextSegment.Next));  
14.	        // replace custom site name with CMS stakeholder HomePage/StartPage URL  
15.	        return regex.Replace(segmentContext.RequestUrl.AbsolutePath, &quot;stakeholder-home&quot;, 1);  
16.	    }  
17.	  
18.	    // check if request path cotains a valid stakeholder custom site name  
19.	    public static bool CheckIfCustomSiteName(RequestContext requestContext, out string stakeholderSiteName)  
20.	    {  
21.	        stakeholderSiteName = null;  
22.	  
23.	        if (requestContext.HttpContext == null)  
24.	            return false;  
25.	  
26.	        // ajax requests got stakeholder ID in request headers  
27.	        if (requestContext.HttpContext.Request.IsAjaxRequest())  
28.	            return false;  
29.	  
30.	        var requestPath = requestContext.HttpContext.Request.Path;  
31.	        var segments = requestPath.Split(new[] { &#39;/&#39; }, StringSplitOptions.RemoveEmptyEntries);  
32.	  
33.	        if (segments.Length &amp;gt; 0)  
34.	        {  
35.	            if (CultureHelper.IsAvailableCulture(segments[0]) &amp;amp;&amp;amp; segments.Length &amp;gt; 1)  
36.	            {  
37.	                stakeholderSiteName = segments[1];  
38.	            }  
39.	            else  
40.	            {  
41.	                stakeholderSiteName = segments[0];  
42.	            }  
43.	  
44.	            return CheckIfStakeholderSiteName(stakeholderSiteName, requestPath);  
45.	        }  
46.	  
47.	        return false;  
48.	    }  
49.	  
50.	    public static bool CheckIfCustomSiteName(SegmentContext segmentContext, string stakeholderSiteName)  
51.	    {  
52.	        return CheckIfStakeholderSiteName(stakeholderSiteName, segmentContext.RequestUrl.ToString());  
53.	    }  
54.	  
55.	    public static void SkipCustomSiteNameSegment(SegmentPair segmentPair, string requestUrl)  
56.	    {  
57.	        var remainingSegments = segmentPair.Remaining.Split(&#39;/&#39;);  
58.	  
59.	        if (remainingSegments.Length &amp;lt;= 0   
60.	            ||!remainingSegments[0].Equals(&quot;DefaultCatalogName&quot;, StringComparison.OrdinalIgnoreCase)   
61.	            || !CheckIfStakeholderSiteName(segmentPair.Next, requestUrl))   
62.	                return;  
63.	  
64.	        segmentPair.Next = remainingSegments[0];  
65.	        segmentPair.Remaining = segmentPair.Remaining.Remove(0, remainingSegments[0].Length + 1);  
66.	    }  
67.	  
68.	    // check if the segment is a valid stakeholder site name  
69.	    public static bool CheckIfStakeholderSiteName(string segment, string requestUrl)  
70.	    {  
71.	        if (segment.IsNullOrEmpty()) return false;  
72.	  
73.	        var key = new Tuple&amp;lt;string, string&amp;gt;(segment.ToLower(), requestUrl);  
74.	  
75.	        // avoid extra checks for already checked segments  
76.	        if (StakeholderSiteNames.ContainsKey(key))  
77.	            return StakeholderSiteNames[key];  
78.	  
79.	        // check for some specific routes to avoid extra call to the database  
80.	        if (segment.Equals(&quot;episerver&quot;, StringComparison.OrdinalIgnoreCase)  
81.	            || segment.Equals(&quot;stakeholder-home&quot;, StringComparison.OrdinalIgnoreCase)  
82.	            || segment.Equals(&quot;DefaultCatalogName&quot;, StringComparison.OrdinalIgnoreCase))  
83.	            return false;  
84.	  
85.	        bool exists = //check if there is a custom sitename in your system equal with the segment   
86.	  
87.	        StakeholderSiteNames.Add(key, exists);  
88.	  
89.	        return exists;  
90.	    }  
91.	  
92.	    // This dictionary help us to avoid extra checks in the same request  
93.	    private static Dictionary&amp;lt;Tuple&amp;lt;string, string&amp;gt;, bool&amp;gt; StakeholderSiteNames  
94.	    {  
95.	        get  
96.	        {  
97.	            if (HttpContext.Current.Items[&quot;StakeholderSiteNames&quot;] == null)  
98.	                HttpContext.Current.Items[&quot;StakeholderSiteNames&quot;] = new Dictionary&amp;lt;Tuple&amp;lt;string, string&amp;gt;, bool&amp;gt;();  
99.	            return HttpContext.Current.Items[&quot;StakeholderSiteNames&quot;] as Dictionary&amp;lt;Tuple&amp;lt;string, string&amp;gt;, bool&amp;gt;;  
100.	        }  
101.	    }  
102.	  
103.	    public static bool CheckIfCatalogContentUrl(string requestUrl)  
104.	    {  
105.	        return requestUrl.Contains(&quot;DefaultCatalogName&quot;);  
106.	    }  
107.	  
108.	    public static IContent TryGetContentRefrence(string url)  
109.	    {  
110.	        try  
111.	        {  
112.	            return UrlResolver.Current.Route(new UrlBuilder(url));  
113.	        }  
114.	        catch  
115.	        {  
116.	            return null;  
117.	        }  
118.	    }  
119.	}  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;Please see below the &lt;strong&gt;CultureHelper &lt;/strong&gt;class:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;1.	public class CultureHelper  
2.	    {  
3.	        public static bool IsAvailableCulture(string cultureCode)  
4.	        {  
5.	            var availableLanguages = GetAvailableLanguages();  
6.	  
7.	            return availableLanguages.Contains(cultureCode);  
8.	        }  
9.	  
10.	        private static IList&amp;lt;string&amp;gt; GetAvailableLanguages()  
11.	        {  
12.	            var languageBranchRepository = ServiceLocator.Current.GetInstance&amp;lt;ILanguageBranchRepository&amp;gt;();  
13.	            var availableLanguages = languageBranchRepository.ListEnabled();  
14.	  
15.	            if (availableLanguages != null &amp;amp;&amp;amp; availableLanguages.Any())  
16.	                return availableLanguages.Select(x =&amp;gt; x.URLSegment).ToList();  
17.	  
18.	            return new List&amp;lt;string&amp;gt;();  
19.	        }  
20.	    }  
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/body&gt;
&lt;/html&gt;</id><updated>2017-11-14T10:03:12.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>