Thursday, July 8, 2010

ASP.NET – Convert Relative URL to Absolute

See my follow up post for an alternative ToAbsoluteUrl() which accepts file-system-relative paths instead of app-relative paths: ASP.NET – Convert Relative URL to Absolute (Part 2).

Having built a blogging engine into my content management system at work, one thing I have to deal with in terms of the blog is making sure that any blog posts with references to website content make use of a fully-qualified, absolute URLs when they are rendered in the RSS feed.

Here’s two extension methods that will convert relative URLs into absolute URLs which include the full host name and port (if applicable). The first method will take a single app-relative URL (~/path/to/foo) and fully-qualify it, while the second method accepts a block of HTML and uses regular expressions to match HTML attributes which contain root-relative URLs (/path/to/foo) and replaces them using the first method.

public static string ToAbsoluteUrl(this string relativeUrl) {
    if (string.IsNullOrEmpty(relativeUrl))
        return relativeUrl;

    if (HttpContext.Current == null)
        return relativeUrl;

    if (relativeUrl.StartsWith("/"))
        relativeUrl = relativeUrl.Insert(0, "~");
    if (!relativeUrl.StartsWith("~/"))
        relativeUrl = relativeUrl.Insert(0, "~/");

    var url = HttpContext.Current.Request.Url;
    var port = url.Port != 80 ? (":" + url.Port) : String.Empty;

    return string.Format("{0}://{1}{2}{3}", url.Scheme, url.Host, port, VirtualPathUtility.ToAbsolute(relativeUrl));
public static string HtmlAppRelativeUrlsToAbsoluteUrls(this string html) {
    if (string.IsNullOrEmpty(html))
        return html;

    const string htmlPattern = "(?<attrib>\\shref|\\ssrc|\\sbackground)\\s*?=\\s*?"
                              + "(?<delim1>[\"'\\\\]{0,2})(?!#|http|ftp|mailto|javascript)"
                              + "/(?<url>[^\"'>\\\\]+)(?<delim2>[\"'\\\\]{0,2})";

    var htmlRegex = new Regex(htmlPattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
    html = htmlRegex.Replace(html, m => htmlRegex.Replace(m.Value, "${attrib}=${delim1}" + ("~/" + m.Groups["url"].Value).ToAbsoluteUrl() + "${delim2}"));

    const string cssPattern = "@import\\s+?(url)*['\"(]{1,2}"
                              + "(?!http)\\s*/(?<url>[^\"')]+)['\")]{1,2}";

    var cssRegex = new Regex(cssPattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
    html = cssRegex.Replace(html, m => cssRegex.Replace(m.Value, "@import url(" + ("~/" + m.Groups["url"].Value).ToAbsoluteUrl() + ")"));

    return html;