Sometimes it seems as if the hardest thing in Episerver is to retreive a proper external url of a PageData or ContentReference item. This especially occurs on multi-language or multi-tenant sites.
The url repository of Episerver may sometimes be complex, and the api's may be changing. What I typically need is a stable method that will be able to resolve the external url of content links no matter which site setup I got. The methods I use have been developed over years and the one I use mostly at the moment seem to do it all.
TLDR; here it is
/// <summary> /// Resolve the absolute external url of a ContentReference /// http://www.herlitz.nu/2016/11/29/get-external-page-urls-on-a-multi-tenant-multi-language-episerver-site/ /// </summary> /// <param name="contentLink"></param> /// <returns></returns> public static class PageDataExtensions { public static string ExternalUrl(this ContentReference contentLink) { var locator = ServiceLocator.Current.GetInstance<IContentLoader>(); var pd = locator.Get(contentLink); var urlString = UrlResolver.Current.GetUrl(contentLink, pd.LanguageBranch, new VirtualPathArguments { ForceCanonical = true }); if (string.IsNullOrEmpty(urlString)) { return urlString; } var uri = new Uri(urlString, UriKind.RelativeOrAbsolute); if (uri.IsAbsoluteUri) { return urlString; } // optional //if (!uri.IsAbsoluteUri && HttpContext.Current != null) //{ // return new Uri(HttpContext.Current.Request.Url, uri).ToString(); //} if (!uri.IsAbsoluteUri) { var siteDefinitionResolover = ServiceLocator.Current.GetInstance<SiteDefinitionResolver>(); SiteDefinition siteDefinition = siteDefinitionResolover.GetDefinitionForContent( contentLink: contentLink, fallbackToWildcardMapped: true, fallbackToEmpty: true); return new Uri(siteDefinition.SiteUrl, uri).ToString(); } return urlString; } }
In a bit more detail
First I construct a PageData object from the contentLink which is required to distinguish the different language branches
var locator = ServiceLocator.Current.GetInstance<IContentLoader>(); var pd = locator.Get(contentLink);
Next up I use the UrlResolver singleton to tech the url for the page, this is where the language specific url is resolved. If no url is detected this is a good place to exit, handle the null return in your call
var urlString = UrlResolver.Current.GetUrl(contentLink, pd.LanguageBranch, new VirtualPathArguments { ForceCanonical = true }); if (string.IsNullOrEmpty(urlString)) { return urlString; }
Check if the urlString was absolute, if it was we are done
var uri = new Uri(urlString, UriKind.RelativeOrAbsolute); if (uri.IsAbsoluteUri) { return urlString; }
If the url still is relative we are probably missing httpcontext, possibly the method is run by a service like an indexer or another timer job. To ensure we construct a correct absolute url we need to fetch the site url from the SiteDefinition which we can construct from the contentLink using the SiteDefinitionResolver
if (!uri.IsAbsoluteUri) { var siteDefinitionResolover = ServiceLocator.Current.GetInstance<SiteDefinitionResolver>(); SiteDefinition siteDefinition = siteDefinitionResolover.GetDefinitionForContent( contentLink: contentLink, fallbackToWildcardMapped: true, fallbackToEmpty: true); return new Uri(siteDefinition.SiteUrl, uri).ToString(); }
Thats it, happy urling.