Click here to Skip to main content
11,567,067 members (47,843 online)
Click here to Skip to main content

ResolveUrl in ASP.NET - The Perfect Solution

, 26 Jan 2010 CPOL 97.7K 71
Rate this:
Please Sign up or sign in to vote.
If you are looking for ResolveUrl outside of Page/Control, and even if you are not, this is for you.

Introduction/Background

From my personal experience using ASP.NET, and from searching the web, I have found that the ResolveUrl method of the Page control (and basically, Control) presents us with some serious problems.

The most common one is that you just cannot use it outside of the page or control context.

Other problems are just bugs. It will not correctly handle some of the URLs you'll give it. For example, try Page.ResolveUrl("~/test.aspx?param=http://www.test.com"). The result is the very same input string... It will just do nothing. By looking into the ASP.NET code using Reflector, I found that all mechanisms that are supposed to convert the relative URLs to absolute URLs will search first for a "://" inside the string, and will return if found. So a query string is OK, unless you pass in a parameter with ://. Yes, I know that the query string parameter should be UrlEncoded, but if it isn't, it is still an acceptable URL. Seriously, check your browsers!

Other suggested methods on the web involve using VirtualPathUtility.ToAbsolute, which is pretty nice and handy, unless you pass in a query string with the URL... Because it will just throw an exception. It will also throw an exception for an absolute URL!

So I decided to find the ultimate solution.

Using the Code

First, I searched for the perfect variable that will give me the Application Virtual Path at runtime without a page context.

I found this to be HttpRuntime.AppDomainAppVirtualPath. It will work anywhere - even inside a timer callback! It gives the path without a trailing slash (ASP.NET makes a special effort to remove the trailing slash...), but that is OK, we can fix it Smile | :)

Then, I did some tests on the original ResolveUrl code, and found where I need to replace what with the AppVirtualPath:

  1. When the URL begins with a slash (either / or \), it will not touch it!
  2. When the URL begins with ~/, it will replace it with the AppVirtualPath.
  3. When the URL is an absolute URL, it will not touch it. (ResolveUrl has a bug with this, as I said before...)
  4. In any other case (even beginning with ~, but not slash), it will append the URL to the AppVirtualPath.
  5. Whenever it modifies the URL, it also fixes up the slashes. Removes double slashes and replaces \ with /.

So I replicated all of that, but without the bugs. And here's the code:

public static string ResolveUrl(string relativeUrl)
{
    if (relativeUrl == null) throw new ArgumentNullException("relativeUrl");
 
    if (relativeUrl.Length == 0 || relativeUrl[0] == '/' || relativeUrl[0] == '\\') 
        return relativeUrl;
 
    int idxOfScheme = relativeUrl.IndexOf(@"://", StringComparison.Ordinal);
    if (idxOfScheme != -1)
    {
        int idxOfQM = relativeUrl.IndexOf('?');
        if (idxOfQM == -1 || idxOfQM > idxOfScheme) return relativeUrl;
    }
 
    StringBuilder sbUrl = new StringBuilder();
    sbUrl.Append(HttpRuntime.AppDomainAppVirtualPath);
    if (sbUrl.Length == 0 || sbUrl[sbUrl.Length - 1] != '/') sbUrl.Append('/');
 
    // found question mark already? query string, do not touch!
    bool foundQM = false;
    bool foundSlash; // the latest char was a slash?
    if (relativeUrl.Length > 1
        && relativeUrl[0] == '~'
        && (relativeUrl[1] == '/' || relativeUrl[1] == '\\'))
    {
        relativeUrl = relativeUrl.Substring(2);
        foundSlash = true;
    }
    else foundSlash = false;
    foreach (char c in relativeUrl)
    {
        if (!foundQM)
        {
            if (c == '?') foundQM = true;
            else
            {
                if (c == '/' || c == '\\')
                {
                    if (foundSlash) continue;
                    else
                    {
                        sbUrl.Append('/');
                        foundSlash = true;
                        continue;
                    }
                }
                else if (foundSlash) foundSlash = false;
            }
        }
        sbUrl.Append(c);
    }
 
    return sbUrl.ToString();
}

Points of Interest

After completing the code and testing over and over again and comparing to the original ResolveUrl, I started to test for performance... In most cases, my code executed faster than the original ResolveUrl by 2.7 times! I also tested inside loops that executed the code 100000s of times on different kinds of URLs.

History

This is the first version, and hopefully the last!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Daniel Cohen Gindi
Software Developer (Senior)
Israel Israel
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionMy vote 5 (Y) Pin
Sameh Saeed Mohammad16-Oct-14 1:21
professionalSameh Saeed Mohammad16-Oct-14 1:21 
QuestionOr why not just use Pin
frno684-Sep-14 21:59
memberfrno684-Sep-14 21:59 
AnswerRe: Or why not just use Pin
Daniel Cohen Gindi4-Sep-14 22:10
memberDaniel Cohen Gindi4-Sep-14 22:10 
BugResolved on full url removed a forwardslash Pin
brother.gabriel17-Oct-13 16:39
memberbrother.gabriel17-Oct-13 16:39 
GeneralRe: Resolved on full url removed a forwardslash Pin
Daniel Cohen Gindi4-Sep-14 22:09
memberDaniel Cohen Gindi4-Sep-14 22:09 
GeneralMy vote of 5 Pin
Naveenlplp20-Aug-13 23:49
memberNaveenlplp20-Aug-13 23:49 
QuestionSystem.Web.VirtualPathUtility? Pin
mwdiablo11-Oct-12 15:51
membermwdiablo11-Oct-12 15:51 
AnswerRe: System.Web.VirtualPathUtility? Pin
Daniel Cohen Gindi11-Oct-12 20:31
memberDaniel Cohen Gindi11-Oct-12 20:31 
GeneralJust what I came here for... Pin
SixOfTheClock4-Aug-12 10:56
memberSixOfTheClock4-Aug-12 10:56 
GeneralRe: Just what I came here for... Pin
Daniel Cohen Gindi4-Aug-12 10:57
memberDaniel Cohen Gindi4-Aug-12 10:57 
SuggestionA suggestion to make it even better Pin
Robbie Couret9-Nov-11 9:55
memberRobbie Couret9-Nov-11 9:55 
GeneralMy vote of 5 Pin
Anurag Gandhi4-Aug-11 20:46
memberAnurag Gandhi4-Aug-11 20:46 
GeneralGreat job! Pin
DotNetWise8-Dec-10 12:23
memberDotNetWise8-Dec-10 12:23 
GeneralMy vote of 5 Pin
ToeStumper1-Sep-10 6:02
memberToeStumper1-Sep-10 6:02 
GeneralRe: My vote of 5 Pin
Daniel Cohen Gindi1-Sep-10 10:02
memberDaniel Cohen Gindi1-Sep-10 10:02 
GeneralGreat job! Pin
ToeStumper1-Sep-10 5:59
memberToeStumper1-Sep-10 5:59 
GeneralRe: Great job! Pin
Daniel Cohen Gindi1-Sep-10 9:56
memberDaniel Cohen Gindi1-Sep-10 9:56 
GeneralMy vote of 5 Pin
Adam Maras26-Jan-10 10:54
memberAdam Maras26-Jan-10 10:54 
GeneralRe: My vote of 5 Pin
Daniel Cohen Gindi9-Apr-10 2:42
memberDaniel Cohen Gindi9-Apr-10 2:42 
General[My Vote of 5] Pin
Chris Carter26-Jan-10 5:25
memberChris Carter26-Jan-10 5:25 
GeneralRe: [My Vote of 5] Pin
Daniel Cohen Gindi26-Jan-10 5:28
memberDaniel Cohen Gindi26-Jan-10 5:28 
General[My vote of 1] Why hack .net find a real solution Pin
RenderEngSol26-Jan-10 0:44
memberRenderEngSol26-Jan-10 0:44 
GeneralRe: [My vote of 1] Why hack .net find a real solution Pin
Daniel Cohen Gindi26-Jan-10 0:53
memberDaniel Cohen Gindi26-Jan-10 0:53 
GeneralRe: [My vote of 1] Why hack .net find a real solution Pin
RenderEngSol26-Jan-10 1:14
memberRenderEngSol26-Jan-10 1:14 
GeneralRe: [My vote of 1] Why hack .net find a real solution Pin
Daniel Cohen Gindi26-Jan-10 1:21
memberDaniel Cohen Gindi26-Jan-10 1:21 
AnswerRe: [My vote of 1] Why hack .net find a real solution Pin
stixoffire30-Jan-10 12:05
memberstixoffire30-Jan-10 12:05 
GeneralRe: [My vote of 1] Why hack .net find a real solution Pin
RenderEngSol1-Feb-10 3:47
memberRenderEngSol1-Feb-10 3:47 
GeneralRe: [My vote of 1] Why hack .net find a real solution Pin
Daniel Cohen Gindi1-Feb-10 7:12
memberDaniel Cohen Gindi1-Feb-10 7:12 
GeneralRe: [My vote of 1] Why hack .net find a real solution Pin
loyal ginger26-Jan-10 6:35
memberloyal ginger26-Jan-10 6:35 
GeneralRe: [My vote of 1] Why hack .net find a real solution Pin
Mike Ellison26-Jan-10 6:16
memberMike Ellison26-Jan-10 6:16 
Generalgood job Pin
Arlen Navasartian21-Jan-10 9:38
memberArlen Navasartian21-Jan-10 9:38 
QuestionOk who edited my article after posting??? Pin
Daniel Cohen Gindi20-Jan-10 5:16
memberDaniel Cohen Gindi20-Jan-10 5:16 
AnswerRe: Ok who edited my article after posting??? Pin
Sandeep Mewara20-Jan-10 8:10
memberSandeep Mewara20-Jan-10 8:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150624.2 | Last Updated 26 Jan 2010
Article Copyright 2010 by Daniel Cohen Gindi
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid