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.
