Fabrice's weblog

Tools and Source

News

My .NET Toolbox
An error occured. See the script errors signaled by your web browser.
No tools selected yet
.NET tools by SharpToolbox.com

Read sample chapters or buy LINQ in Action now!
Our LINQ book is also available on AMAZON

.NET jobs

Emplois .NET

Tuneo

ASP.NET Hosting transatlantys

Contact

Me

Others

Selected content

Archives

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="http://weblogs.asp.net/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?

Comments

Linus Concepcion said:

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 <% %> 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.
# May 5, 2004 11:07 AM

AndrewSeven said:

I use ~ for server controls and a property on my base class that exposes Request.ApplicationPath or a value from Commerce Server
# May 5, 2004 11:21 AM

Paul Wilson said:

<%=Request.ApplicationPath%> 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 ...
# May 5, 2004 12:08 PM

Fabrice said:

Paul, I was almost sure the <%=Request.ApplicationPath%> trick was not faster than the runat="server" one. It would be interesting to do some benchmarking to know how much it costs to add runat="server" on a standard page. I'll try to do that.
# May 5, 2004 12:26 PM

karl said:

<IMG src="<%=Request.ApplicationPath%>/Images/imgxx.png">

will result in:
src="//Images/imgxx.png" 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='<%=ResolveUrl("../../Images/Spacer.gif")%>'


ASP.Net forums 2.0 (beta) do a number of different things (shame on them), but the best is probably:
src='<%=Globals.GetSkinPath()%>...' 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 == "/") {
return string.Empty;
} else {
return applicationPath;
}
}

}


Always ask urself, what would jebus do?

# May 5, 2004 12:42 PM

Jerry Pisk said:

I personally use ResolveUrl("~/..."), 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.
# May 5, 2004 3:42 PM

Jon Galloway said:

This gets more complex when rewriting url's and inheriting from a common page base. We set the image root in the HttpContext the global.asax.cs, used <img runat=server ...>, and set the image directory in page or page base code. I think we had problems with ResolveUrl, but I'm not certain.
# May 5, 2004 6:32 PM

Kyle Heon said:

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.
# May 6, 2004 7:47 AM

Fabrice said:

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.
# May 6, 2004 7:52 AM

sami said:

"Wouldn't it be possible to use the <BASE> tag?"
This is my prefered one :-)
# May 7, 2004 4:50 PM

Sam said:

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

Richard Boles said:

What about using "<%=HttpRuntime.AppDomainAppVirtualPath%>/Images/MYImage.gif" 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.
# July 15, 2004 1:02 PM

Nick Gilbert said:

I use this:

<base href='<%# Request.Url.Scheme + "://" + Request.Url.Host + Request.ApplicationPath + (Request.ApplicationPath.EndsWith("/") ? "" : "/") %>'>

It's a big long-winded but it seems to work. I then also override the "render" method of the FORM so it doesn't output the "action" attribute which gets rid of the problem mentionned by Sam.
# July 23, 2004 11:09 AM

Monty said:

I have a key in my Web.Config file called "BaseURLForSite", so I get my path like this:

<LINK href="<% =ConfigurationSettings.AppSettings("BaseURLForSite") %>styles\site.css" type="text/css" rel="stylesheet">

and images like this:

<IMG src="<% =ConfigurationSettings.AppSettings("BaseURLForSite") %>images/InfoIcon.gif" align="left" vspace="5">

Seems to work so far, but I'm happy to hear critiques on the method if someone sees something wrong with it...
# August 6, 2004 11:59 AM

Fabrice said:

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.
# August 6, 2004 1:46 PM

Brian said:

Has any one tried using a Skin for this?

Seems to work fine for me.
<asp:image SkinID="Spacer" runat="server" width="10" height="10" />

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.

# June 5, 2006 4:55 PM

shardul said:

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

# June 27, 2007 5:03 AM

Peter O said:

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/"

# September 13, 2007 3:24 PM

Leon said:

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

# October 30, 2007 11:05 PM

Ruffs said:

Can you not use ./

Example:

<IMG src="./images/imgxx.png">

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?

# December 3, 2007 10:58 AM

Krakke said:

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

# January 31, 2008 5:42 AM

Mitosis said:

<IMG src="./images/imgxx.png">

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.

# March 25, 2008 11:11 AM

Mike N. said:

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: <IMG src="~/Images/imgxx.png">.

# June 26, 2008 10:09 AM

Fabrice Marguerie said:

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

# June 26, 2008 10:31 AM

David Hillman said:

Using runat="server" doesn't work for flash and other resources that use <object> because the classid="clsid:"

will cause an exception.  I use the ResolveUrl trick and it seems to work.

# September 11, 2008 9:54 AM

Marc Wickens said:

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?

# December 31, 2008 1:07 PM

Fabrice Marguerie said:

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.

# January 4, 2009 9:35 AM

Shafeeqw said:

How do you make this work with linking a stylesheet?

# March 23, 2009 12:23 PM

Monica Chaturvedi said:

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

# July 7, 2009 1:10 PM

Karl Wenzel said:

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:

<base id="baseHref" runat="server" />

c# code in MasterPage Page_Load event:

baseHref.Attributes["href"] = Request.Url.AbsolutePath;

# February 16, 2011 12:04 PM

Kamleshkumar Gujarathi said:

Usally!!!

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

Exemple: <img runat="server" src="~/Images/imgxx.png" />.

But in some of cases, it seems that it appends the path along with tilde sign.

eg. virtualpath/web/~/web/mypage.aspx

:)

Kamleshkumar Gujarathi.

# April 12, 2011 5:30 AM

Kamleshkumar Gujarathi said:

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);    

       }            

   }  

# April 12, 2011 5:47 AM

Tamara said:

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...

# September 16, 2011 4:48 PM

nuwan said:

@Linus Concepcion

you can use  <link  type ="text/css"  rel="stylesheet" href="<%= Request.ApplicationPath  & "Css/DNCRMCommon.css" %>"  />  

# October 4, 2011 7:20 AM

Eduardo de Souza said:

Simple and neat:

<base href="<%=Request.ApplicationPath%>/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

# October 4, 2011 12:23 PM

Andrew Weitzen said:

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.

# January 25, 2012 9:32 AM