ASP.NET Hosting

Avoiding problems with relative and absolute URLs in ASP.NET

I'm back to ASP.NET development, and the first thing that hits me in the face is the limitations with URL handling in ASP.NET.

I have an ASP.NET application which pages contain images. The images are represented using HTML's IMG tags. This is a common situation, I guess :-) The problem is simple: how should I express the URLs of my images?

I can think of five options:

  • use relative paths.
    Problems: If you move your files around, you'll need to change every path. If you use this in a User Control, the path will be relative to the page using the control, not to where the control file is.
  • use absolute paths.
    Exemple: <img src="/Images/imgxx.png" />
    Problem: This requires a specific web site for each application. Apart from being an additional difficulty for deployment and administration, this isn't possible on Windows XP, which allows only one IIS web site.
  • use a specific alias for the images.
    Exemple: one alias named MyApp for the application, one alias named MyAppImages for the images, and images referring to /MyAppImages/imgxx.png.
    Problem: multiplicating aliases can be a problem and add to the complexity of the application.
  • use ~ and runat="server" everywhere.
    The tilde (~) character represents the root directory of the application in ASP.NET.
    Exemple: <img runat="server" src="~/Images/imgxx.png" />.
    Problem: Performance is hurt because your image tags are converted to server controls when the page needs to be generated, while there isn't really a need for this.
  • switch to Image web controls and use ~.
    Problem: same as above, plus the fact that it makes converting from HTML pages to ASPX pages more difficult.

The solution I will probably choose is a sixth option: use <%=Request.ApplicationPath%> to prefix the URLs. Exemple: <img src="<%=Request.ApplicationPath%>/Images/imgxx.png" />

Which way do you go? Do you have a better solution?

35 Comments

  • I didn't even know about Request.ApplicationPath. This is certainly useful, and probably is the right way to go.



    However, what about .css files, or .js files that cannot use the &lt;% %&gt; escape notation but need to refer to other files. Unfortunately, relative paths don't work because the paths are not relative to the .css or the .js, but relative to the .aspx file that uses them.

  • I use ~ for server controls and a property on my base class that exposes Request.ApplicationPath or a value from Commerce Server

  • &lt;%=Request.ApplicationPath%&gt; is basically parsed into a server control, so you really aren't avoiding the tiny performance hit that you were hoping to avoid. I used to use a version of this myself, but I finally gave in and just use ~ and runat=server. I stick to the basic tag versions if I know that's the html I want, while using the asp:* version if there may be something to gain in letting the html vary by browser. This isn't that big of a deal now, actually sometimes being worse for Mozilla right now, but the asp:* controls are getting really nice adaptive rendering in Whidbey, so ...

  • Paul, I was almost sure the &lt;%=Request.ApplicationPath%&gt; trick was not faster than the runat=&quot;server&quot; one. It would be interesting to do some benchmarking to know how much it costs to add runat=&quot;server&quot; on a standard page. I'll try to do that.

  • &lt;IMG src=&quot;&lt;%=Request.ApplicationPath%&gt;/Images/imgxx.png&quot;&gt;



    will result in:

    src=&quot;//Images/imgxx.png&quot; when the application is at the root of the website. I'm not sure if its technically bad, but I sure don't like it.



    In situations like this, I always look at sample code provided by Microsoft.



    Community Portal uses:



    src='&lt;%=ResolveUrl(&quot;../../Images/Spacer.gif&quot;)%&gt;'





    ASP.Net forums 2.0 (beta) do a number of different things (shame on them), but the best is probably:

    src='&lt;%=Globals.GetSkinPath()%&gt;...' with GetSkinPath basically calling a custom ApplicationPath + Theme path (as defined in web.config).





    static public string ApplicationPath {



    get {

    string applicationPath = HttpContext.Current.Request.ApplicationPath;



    // Are we in an application?

    //

    if (applicationPath == &quot;/&quot;) {

    return string.Empty;

    } else {

    return applicationPath;

    }

    }



    }





    Always ask urself, what would jebus do?



  • I personally use ResolveUrl(&quot;~/...&quot;), and I even use it for server side controls, because most of them will output relative paths instead of an absolute ones which is causing all kinds of problems when using URL rewriting.

  • On a recent project I created a base UserControl that all of my other UserControls inherit from. In this base control I have properties for BaseUrl, a SqlConnection, and anything else that may need to be shared across all UserControls. This has so far worked pretty well. I don't have duplicated code in any of my UserControls.

  • Kyle, not too sure having a SqlConnection in a UI component is a good practice. But having base classes for controls and pages sure is.

  • &quot;Wouldn't it be possible to use the &lt;BASE&gt; tag?&quot;

    This is my prefered one :-)

  • I like the &lt;BASE&gt; tag also, but it is causing problems with .NET forms when the .NET webform is not in the &lt;Root&gt; path. ie. If Login.aspx was in &lt;root&gt; path, when it submits, it works fine, BUT if Login.aspx was located at: &lt;root&gt;/MyPages/Login.aspx - by using the &lt;BASE&gt; tag, when Login.aspx submits, it drops off &quot;MyPages&quot; and returns me to &lt;root&gt;/Login.aspx. Anyone know a way around this? Otherwise, I'll have to take out the &lt;BASE&gt; tag and use one of the methods described earlier.

  • What about using &quot;&lt;%=HttpRuntime.AppDomainAppVirtualPath%&gt;/Images/MYImage.gif&quot; for the HTML client tags?

    Or even better set up a virtual web directory with the images, this will handle both client and server side images.

  • I use this:



    &lt;base href='&lt;%# Request.Url.Scheme + &quot;://&quot; + Request.Url.Host + Request.ApplicationPath + (Request.ApplicationPath.EndsWith(&quot;/&quot;) ? &quot;&quot; : &quot;/&quot;) %&gt;'&gt;



    It's a big long-winded but it seems to work. I then also override the &quot;render&quot; method of the FORM so it doesn't output the &quot;action&quot; attribute which gets rid of the problem mentionned by Sam.

  • I have a key in my Web.Config file called &quot;BaseURLForSite&quot;, so I get my path like this:



    &lt;LINK href=&quot;&lt;% =ConfigurationSettings.AppSettings(&quot;BaseURLForSite&quot;) %&gt;styles\site.css&quot; type=&quot;text/css&quot; rel=&quot;stylesheet&quot;&gt;



    and images like this:



    &lt;IMG src=&quot;&lt;% =ConfigurationSettings.AppSettings(&quot;BaseURLForSite&quot;) %&gt;images/InfoIcon.gif&quot; align=&quot;left&quot; vspace=&quot;5&quot;&gt;



    Seems to work so far, but I'm happy to hear critiques on the method if someone sees something wrong with it...

  • Monty, I think keeping the value of BaseURLForSite in a static variable somewhere would be better as far as performance is concerned, instead of looking each time (several times per page!) into ConfigurationSettings.AppSettings.

    Also, using Request.ApplicationPath removes the need to add anything to the configuration file, and so makes it easier to deploy the web application on different servers.

  • Has any one tried using a Skin for this?



    Seems to work fine for me.

    &lt;asp:image SkinID="Spacer" runat="server" width="10" height="10" /&gt;



    Note the absence of id on the control, one seems to be generated at runtime if none is defined.



    I've not verified the impact on performance at the server, but sure is convienent for the developper.



  • I am having a pop up window, which has a combo. On change of combo XML file is read at a location in virtual directory. But once combo change event is fired it is unable to find the xml file. I have given the path as "~/testApp/test/xml". it takes it as c:\Windows\System32\inetsrv\~\.........

    Please help

    Thanks in advance

  • Consider not embedding images with <img elements in the html file in the first place. Rather use your css background-image on the tag. The css file will always be related to the image folder in a constant way and if you move any of those folders, do a find and replace on the same css file like so: find "images/" and replace with "new_location/images/"

  • What should I do if I'm trying to load static images from a subdomain?

  • Can you not use ./

    Example:


    This works well in all XP developed websites I've worked on, deploys well be it a virtual directory application on a webserver, or it being the root application for a domain, because all references work from that base directory your application uses?

  • Ruffs,.. OMFG why did I not think of that?? You just solved all my problems hahah,.. great, thanks alot!!



  • Does not work for me. I have a web application, not a website. I am using the ~ and runat="server" solution, that works.

    God, I hate web development.

  • When using the tilde (~) character and the web page can not find the image... where do you set the root directory of the application in IIS?
    Exemple: .

  • Mike, you need to add runat="server" to your img tag. I've just updated the post to make this clear.

  • Using runat="server" doesn't work for flash and other resources that use because the classid="clsid:"
    will cause an exception. I use the ResolveUrl trick and it seems to work.

  • I am using Request.ApplicationPath in a loop to build my menu.

    I wonder if it's better to place Request.ApplicationPath into a String variable before starting the loop, or does the .NET runtime optimise that method so it doesn't need to be calculated more than once?

  • Marc, the best way to find the answer to this kind of questions is to use .NET Reflector to disassemble the property you're interested in - Here, Request.ApplicationPath.

  • How do you make this work with linking a stylesheet?

  • Hi Shafeeqw,

    Base tag is the ultimate solution for url rewriting. For stylesheet linking, you can do any of the following three things:
    1. call your css before you write base tag in head
    2. use absolute path for css
    3. use css href relative to base href.

    Regards
    Monica Chaturvedi

  • I was having trouble with relative links because of URL rewriting. I used the recommendation that Monica Chaturvedi made, except rather than hard-coding the URL, I made it dynamic.

    asp code in MasterPage:


    c# code in MasterPage Page_Load event:
    baseHref.Attributes["href"] = Request.Url.AbsolutePath;

  • Usally!!!

    The tilde (~) character represents the root directory of the application in ASP.NET.
    Exemple: .

    But in some of cases, it seems that it appends the path along with tilde sign.
    eg. virtualpath/web/~/web/mypage.aspx

    :)
    Kamleshkumar Gujarathi.

  • public class LocationHelper
    {
    public static string ImgUrl(string imageFileName)
    {
    return VirtualPathUtility.ToAbsolute("~/Content/images/" + imageFileName);
    }

    public static string RegisterCss(string cssFileName)
    {
    var sPath = VirtualPathUtility.ToAbsolute("~/Content/css/" + cssFileName);
    return string.Format("", sPath);
    }

    public static string RegisterCssWithAbsPath(string cssLocation, string filename)
    {
    string path;
    if (cssLocation.StartsWith("~"))
    {
    path = cssLocation;
    }
    else
    {
    path = "~" + cssLocation;
    }
    cssLocation = VirtualPathUtility.ToAbsolute(path) + "/" + filename;
    return string.Format("", cssLocation);
    }

    public static string RegisterScript(string scriptFile)
    {
    string scriptRoot = VirtualPathUtility.ToAbsolute("~/Scripts");
    string scriptFormat = "";
    return string.Format(scriptFormat, scriptRoot, scriptFile);
    }
    }

  • I have been reading all the posts, and I really got
    confused..! So this is my first real web site, and
    what should I use,best: Request.ApplicationPath..?
    Because I have been using the ~ in all my website,so far...

  • @Linus Concepcion

    you can use <link type ="text/css" rel="stylesheet" href="" />

  • Simple and neat:

    <base href="/site/" />

    I do the rewrinting in Global.asax, so it work in any IIS, and images / js / css I handle with base.

    I like this solution because, my dev environment is not always like production.

    Cheers

  • In Visual Studio, in Solution Explorer, right click your application, select Properties Window and change the Virtual Path to /.

    Now your development and production environments will work the same.

    You do not have to use ~.

    You do not have to run any of urls through code.

Comments have been disabled for this content.