Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

There is something about Paths for Asp.net beginners

0.00/5 (No votes)
5 Jan 2011 1  
Clarifying “Path” related issues for beginners in Asp.net

Introduction

What’s so important about "paths"?

October, 2004

A young developer was struggling with the hardest problem on earth. He was trying to show an image in two different web pages. The image was appearing on one web page, and, the same was not appearing on another!

Following was the HTML element he was struggling with:

<img id="imgAction" src="images/action.jpg" />

Guess what, the poor developer was none but me!

I still can remember, I had troubles with setting appropriate paths for different elements(Images/documents/videos etc) in the web pages. I used to struggle with putting download links in the pages and show images and/video files (Which I still have to do) by using different HTML elements or server controls along with setting their different attributes (href, src etc) values. It sounds funny, but, often I used to find myself in situations where the images were appearing in one page and not appearing in another Or, the download links were braking often. I wondered why!

That was a great mastery to me in those early days, along with lots of other mysterious things I was going through each day.

Its obvious, I didn’t have clear understanding about setting the href or src property values appropriately for the HTML and Asp.net server controls, and that’s why I had to suffer.

Its been long, those days are gone. I grew up a little, and, I don’t have any problem with those mysterious paths these days. Still today, I see lots of newbies struggle with these path related issues (See here and here) and these hurts. So, I decided to uncover those “path mysteries” for the absolute beginners (Like me in October 2004) so that, they don’t have to waste a minute for setting appropriate path values for Asp.net pages.

Please note that, this article doesn’t discuss any “rocket science”. All It talks about is some plain old basic stuff and great chance is, you already are a master of the subject matter. But, I often love to go back to the basics, and try to sharpen my knowledge, to make myself a little better then yesterday. Who knows? someone might just feel the same. If you are one of those, feel free to proceed.

What did I mean by "Path"?

In an Asp.net applications, we either use HTML elements or Server controls for adding several elements (Such as, Image, HyperLink etc.) in the pages. For an <img> element, we need to set an “src” attribute value (Usually, in terms of relative URL path) that points to an image within the application in general. Also, for an anchor <a> element, we need to set an “href” value that points to a resource (Be it a page or an image or a downloadable zip file) within the application directory. These are the attributes that I am referring as “path” in general.

Well, these look pretty straight-forward. But, its some of those cases, you may have found yourself not making those work and you wondered how. This article will try to address those issues and reveal all mysteries.

For most kind of HTML element, there is a corresponding Server control in Asp.net. Naturally, the server controls also have corresponding attributes that accepts the path values as relative URLs, and, set those into the HTML element’s path attribute that they render in the browser. So, like the HTML elements, there are path related issues for server controls too. As a result, understanding the basic path related issues for HTML elements first will make it easier for us to understand the Server control related path issues later.

How browser loads elements containing “path” attributes?

In HTTP, user hits a URL in the browser (Or, does something that hits a URL, such as clicking a Link or a Button) and an HTTP request is sent over the wire to the specified web URL. The server in turn generates some response (Mostly in the form of HTML markups) that the browser receives and interprets it into the user interface in terms of some understandable output.

Once the HTML markups are generated from the server for a page, browser loads the HTML first and once its is done loading, it starts loading the elements in the HTML markup that have “path” attributes. For each of those such elements (Such as, <img>, <a> <link> <script> etc), browser sends an asynchronous request again to the server with the URL that is provided in their “path” attributes (src or href). Asynchronous request means, browser sends the requests “behind the scene”, and, doesn’t wait for the response from server while doing other activity. So, the UI remains responsive to the user, and, when the response is received from the server, the UI is updated with the response data.


ImageLoading.png

Figure : Asynchronous loading of images in browser

Now, most of the cases, the URL values provided to these HTML elements are relative, because, the resources requested by the URL resides within the web application’s folder structure. While browser loads these relative paths, it calculate the absolute URL paths by combining the host application’s base URL plus the relative URL and sends the request to the same host application. But, if for any reason the resources (Image files, javascript or css files or files to be downloaded by users) are kept within some different web applications and served from there, (That is, these are not kept within the web application's root folder) the URL values contain some absolute paths. These cases, browsers send asynchronous request to the absolute URL provided in the path attributes and receives responses and update the UI output. This is mostly done for optimizing performance of web page loading at user’s browser (Because, from the browser HTTP allows to use only two simultaneous connections to use for each domain address and hence, it sometimes take too much time to load all markups and resources from a single host web application).

Based upon this relative path, browser calculates an absolute path and sends a request to the server to obtain the actual resource and render or loads in the browser. Now, if for any reason the src value is set inappropriately, the browser will not be able to determine the correct URL for the resource, and consequently the URL request will fail and the corresponding resource will not be loaded or appear in the page.

So, let us try to understand how browser actually determines the absolute image url based upon the relative url set to the src attribute.

Path calculation basic rule

As has been said already, images and other resources are mostly kept within the web application's root folder, in a designated directory and we usually provide a relative path of the images (And other resources) so that browser can calculate the absolute image URL using the provided relative path as the src attribute value. We can of course set absolute URLs for all images and resources in our application (Assuming that, the resources resides within the web application’s root folder structure), but, this is highly discouraged because in case the site URL is changed later for some reason, all of the src attribute values will needed to be changed. That would be horrible. (Absolute URLs are only used if the resources are kept in different server for optimizing performance, those servers are called Content Delivery Network-CDN).

Now, let us assume we have a page http://www.mysite.com/default.aspx, which contains an <img> elemtn with the following src attrubte value:

src = "/images/action.jpg"

As we can guess, within the web application’s root folder, there is an “images” folder where there is an “action.jpg” file. While encountering this element, browser will try to determine an absolute URL (http://www.mysite.com/images/action.jpg) and send an asynchronous request to this URL. If the web server finds the image at this location, it will read and send the image in the response and browser shows it in the output.

A little complexity

Not all pages are implemented directly within the web application's root folder and its pretty common to implement pages under some folder structure. So, let us assume we have a following situation in our application:

The currently browsed page is : http://www.mysite.com/pages/default.aspx,

The img src in the page is set as : src = "/images/action.jpg"

This will fail to load the image and show in the browser. Why? well, its because, the browser now calculates the absolute image path as follows:

http://www.mysite.com/pages/images/action.jpg

And, as the image is not available in the above URL path, browser will fail to display the image, because, the correct image URL should be http://www.mysite.com/images/action.jpg.

So, how to fix this URL path?

You can fix it in two approaches, one approach is you use a relative path based upon the current directory of current page and another is you use a relative path based upon the site’s base URL.

Relative path based on current directory:

Let us assume the following scenario:

Current directory of your page is :http://www.mysite.com/pages/And, directory of requested image is : http://www.mysite.com/images/

So, for calculating the correct relative path of the image from within the page, you should go one level up (by using "..") from the "pages" directory and then add the relative path of the image by adding "/images/action.jpg". So, the corrected src should be set as follows this time:

src = "../images/action.jpg"

And, based upon the above relative path from within the /pages folder, browser will be able to determine the correct absolute image URL as follows:

http://www.mysite.com/images/action.jpg

And, the image will be shown in the browser.

Relative path based on Site’s base URL:

Alternatively, you can use an image path that would let the browser calculate the absolute url based upon the web site's base URL address. For this, you have to set the src value as follows:

src = "/images/action.jpg" (Note that, the path starts with a "/" this time)

This will result in the browser calculating the absolute path (Relative to the root directory) as follows, no matter in what folder structure your page is under the web root folder.

http://www.mysite.com/images/action.jpg

So, the browser will be able to display the image successfully in the browser.

CAUTION!

The 2nd strategy should be used only if the web application is deployed under a web site, rather than a virtual directly in the IIS (A Virtual directory is a directory/path name that is appended after the web site’s base URL, pointing to a web application's root folder. There could be multiple virtual directories deployed under a site, each pointing to a different Asp.net application code base.). The reason is, browser calculates the full URL path based upon the web site's root URL (Not the virtual directory's root url) plus the relative url.

So, if we had a web application deployed under a virtual directory as follows:

http://www.mysite.com/app/pages/default.aspx (app is the virtual directory here)

And, if we set the src attribute of an image element in this page as follows:

src = "images/action.jpg"

Browser will calculate the absolute URL as follows (Using only the site URL):

http://www.mysite.com/images/action.jpg

And, the above URL request will fail this time because, the correct image URL should actually be as follows:

http://www.mysite.com/app/images/action.jpg

Path related issues with Asp.net Server controls

Enough learning on paths and URLs with HTML elements, and by this time we have had a solid understanding about how the browsers determine URL paths for the HTML elements. We are now knowledgeable enough to understand the path related issues that might happens with Asp.net Server controls.

Almost every HTML element has a corresponding Asp.net Server control. For example, for HTML <img> element, Asp.net has the following server control:

<asp:image id="Image1" ImageUrl="~/image/action.png" runat="server"/>

The above server control actually renders an <img> HTML element in the browser, and, the ImageUrl property value is set to the src property value of the <img> element. Note that, when you set the ImageUrl's property value in Visual Studio, you usually set by using "Pick URL" visual studio intellisence.
SelectImage.png

Figure : Visual Studio intellisence for picking ImageUrl property

And, if the path is set by clicking the "Pick URL" for any server control, visual studio sets the ImageUrl property value with a path starts with the character "~" (Tilde).

What is "~"?

"~" (Tilde) is a special character that is usually used to set URL paths for Asp.net Server controls and this character instructs the Asp.net run time to resolve the relative path of the server control.

Not clear? Let’s see some examples to make it easy:

Example1:

Say, we have an web page at the following URL :

http://www.mysite.com/pages/default.aspx

This page contains an <asp:Image /> element with the ImageUrl value set as :

</asp:Image ImageUrl="~/image/action.png" id="Image1" runat="server"/>

So, when this page is requested by the browser, the Asp.net runtime will replace the "~" with the relative path that navigates the browser from the current folder(http://www.mysite.com/pages/) to the web application's root folder (http://www.mysite.com/) ,that is, one directory up ( ".." ), to build the correct relative URL of the img element.

And hence, the full relative URL be resolved as : "~/image/action.png" = “../image/action.jpg"

Example2:

Say, we have another web page at the following URL :

http://www.mysite.com/default.aspx

This web page also contains an <asp:Image> element with the ImageUrl value set as :

ImageUrl="~/image/action.png"

So, when this page is requested by the browser, the Asp.net runtime will replace the "~" with the relative path that navigates the browser from the current directory (http://www.mysite.com/) to the web application's root directory (http://www.mysite.com/). That is, with a blank ( "" ) as both current directory paths are same now.

And hence, the full relative URL be resolved as : "~/image/action.png" = "/image/action.jpg"
Based upon the relative URL’s in either cases, browser will be able to determine a correct absolute URL for the image and hit an asynchronous request to the server to load the image and show in the output.

User control related path problem: A classic issue

Suppose our Asp.net web application has an organized folder structure for aspx pages and user controls (ascx), for laying out the pages and controls in a logical structure within the root folder.

Let us assume, we have a following scenario:

A user control is located at : /UserControls/Products/UCProductDetails.ascx

The user control has an HTML img element : <img src=""..> (The src is not set yet)

A page is located at :/Pages/Products/ProductDetails.aspx, which uses this user control.

Another page is located at : /Pages/Home.aspx, which also uses this user control.


UserControl.png

Figure : Accessing User control from multiple aspx at different locations

Note that, the <img> element is being used within the user control. No matter what the user control's location is within the folder structure, when the page is requested from the browser, the Asp.net runtime loads the page along with the user control, executes it and sends the HTML output to the browser. So, at browser’s end, there is only HTML markup, and the browser will simply try to determine the relative path of the image based upon the current location of the page.

Note that, the user control is used in both aspx page. So, if the currently browsed page is

http://www.mysite.com/Pages/Products/ProductDetails.aspx,

The src value of the <img> element should be set as:

src = "../../images/details.jpg" (Because, browser has to navigate two folder up and then search the image within the “images” folder.

But, if the currently browsed page is http://www.mysite.com/Pages/Home.aspx,

The src value of the <img> element should be set as:

src = "../images/details.jpg" (Because, browser has to navigate now one folder up to access the image within the “images” folder.

Huston, we have a problem :(

To solve this, the correct relative URL of the <img> element within the user control has to be determined based upon folder location of the requested page. Fortunately, we don’t have to do this as we have our old friend “~” (Tilde). All we have to do is to replace the HTML <img> element with an Server control and set the ImageUrl property that starts with "~" (Tilde). That is:

ImageUrl = "~/images/details.jpg"

Or, we should turn the <img> element to a server control as follows:

<img runat="server" id="imgDetails" src="~/images/details.jpg" />

In either case, Asp.net application will be able to calculate the appropriate relative directory at the server-end, based upon the location of the currently requested page, no matter where the page or user control is within the folder structure of the web application.

What about other elements?

Not only image elements/controls, but, there are other HTML elements and Asp.net Server controls also, which will have the same path related issues when used within the user controls and, the same logic should be applied to them to solve the issues.

Unfortunately, not all HTML elements have a corresponding server control in Asp.net. For these kinds of elements, adding the runat=”server” attribute and set the path starting with “~” (Tilde) will not work. Why? because, adding the runat="server" converts these elements as HtmlGenericControl at the server-end, and, this type of object doesn't have anything to resolve the Tilde(~) operator to determine the correct relative URL.

For these kinds of elements, Page.ResolveClientUrl() is the perfect solution.

For example, let’s say a user control has the following HTML elements:

<script src="../js/common.js" type="text/javascript" language="javascript"/>
<link rel="Stylesheet" href="../style/common.css" type="text/css" />

As this user control could be used from any web page within the web application, there will be path related issues with these <script> and <link> elements also (If paths properties are set for one page appropriately, the same path will not work other pages in different directory ).

To solve this issue, the above elements should also be modified with the ResolveClientUrl() as follows: 

<script src='<%=ResolveClientUrl("~/js/common.js")%>' type="text/javascript" language="javascript"/>
<link rel="Stylesheet" href='<%=ResolveClientUrl("~/style/common.css")%>' type="text/css" />

Path related issues in the Code behind and back-end

Up to now we’ve learnt how the Asp.net runtime resolves relative paths for HTML elements and Server controls and what are the best practices for setting paths appropriately, in different situations. One important thing to notice, in all cases, we've discussed setting appropriate paths in the XHTML markups, not in the CodeBehind files.

Control.ResolveUrl(string path)

We can of course set path/urls at the XHTML markups if the paths/urls are well known in design time. But, if for any reason if the path/url is not known in advance (Say, the path/url will vary based upon some conditions) or simply if you are more interested to set the paths/urls in the back-end, you need to know a programmatic way for resolving correct relative paths.

At the most basic level, each Server control has a ResolveUrl() method (Inherited from the base Control class), that accepts a string path/url value. So, if you have a Server control as follows:

<asp:Image ID="Image1" runat="server" />

You can set the ImageUrl property in the CodeBehind as follows:

Image1.ImageUrl = Image1.ResolveUrl("~/images/action.jpg");
Or simply,
Image1.ImageUrl = “~/images/action.jpg”;

This assumes that the action.jpg is available in this relative path : /images/action.jpg. Note that, you have to supply a "root relative" path that starts with the Tilde ("~"), to get the correct relative path.

I explored the HTML markup using FireBug, that is rendered for setting the ImageUrl in the markup, and found that, the HTML is a little difference with the HTML that is rendered for setting ImageUrl using Image1.ResolveUrl("~/images/action.jpg"); in the CodeBehind, which is interesting. Here is the difference:

HTML generated for setting ImageUrl at Markup:

<img style="border-width: 0px;" src="../images/action.jpg" id="Image1">

HTML Markup generated for setting ImageUrl at CodeBehind:

<img style="border-width: 0px;" src="/WebSite3/images/action.jpg" id="Image1">

Note that, the two URLs are little bit different. First one is relative in terms of current page location within /Pages/ and Second one is relative in terms of web application's root folder. But, in either case, the request image will be rendered from correct location in the browser.

As a Page or a User control is also a control, that means, you can also call the ResolveUrl() in the following way also:

Image1.ImageUrl = this.ResolveUrl("~/images/action.jpg");
Or, simply
Image1.ImageUrl = ResolveUrl("~/images/action.jpg");

VirtualPathUtility.ToAbsolute(string path)

As each control has the ResolveUrl() method, it is easily possible to invoke it and get the correct relative path for any server control (By supplying the root-relative path that starts with the Tilde "~" operator). But, what if you have to resolve a relative path from within the HttpHandler or a HttpModule, or even, from within a class library?

You won’t get the Control objects within the Httphandler or HttpModule. So, you can’t call the ResolveUrl() method. Fortunately, there is an easy way. You can use the following method that accepts the same parameter argument (Like ResolveUrl()).

VirtualPathUtility.ToAbsolute("~/images/action.jpg")

Along with this method, the VirtualPathUtility has many other utility methods also that aid in calculation of various path related logic, to make our life easier.

Getting the physical file paths from relative URLs

Determining the physical location of a file, based upon a relative URL path is a pretty common need. You might have a requirement of reading and writing files stored within the web application folder and you only have the relative path/URL of the file, not the physical file location. What would you do?

Fortunately, we have a good old friend, the Server.MapPath().

When you call the Server.MapPath() with a relative path/URL, it returns the complete physical location of the file that is stored within the web application folder. Let us see some examples:

Suppose our web application’s root folder location is as follows:

C:\applications\aspnet\www.mysite.com\

string RootPath = Server.MapPath(“~”)

This returns the path “C:\applications\aspnet\www.mysite.com\”

string FilePath = Server.MapPath(“~/images/search.jpg”

This returns the path “C:\applications\aspnet\www.mysite.com\images\search.jpg”

Please note that, the physical path of the file/folder that is returned by the Server.MapPath() does not guarantee that the file or folder exists there physically. It just maps the supplied relative path to a physical path location based upon the web application’s root directory. It’s your responsibility to ensure that the physical location of the file or folder actually exists ( Using System.IO.Path.Exists(FilePath) ), before you proceed the next step.

Path properties in Request object

There are some important Path properties in the Request object that are kind of “Must know” for Asp.net geeks. Here are these:

Request.ApplicationPath

This returns the Application’s path relative to the Site’s path.

Please note that, the “Application” can be a virtual directory or the site itself. So, if the current application is deployed under a site, for a URL http://www.mysite.com/home.aspx, this would simply return “/”

And, if the current application is deployed under a virtual directory, for a URL http://www.mysite.com/account/home.aspx, (Where account is the virtual directory name) this would return the path “/account”

This property is handy if you need to calculate a relative URL of any resource within the application, if you know where in the web application this resource exists.

Request.FilePath

Returns the relative or virtual path of the current request, in terms of the site’s URL. That is, for a URL http://www.mysite.com/account/home.aspx, this would return /account/home.aspx. It doesn’t matter whether the application is a virtual directory or a site.

Request.CurrentExecutionFilePath

Same as Request.FilePath (See above), except the fact that, it returns the path even if the current page is executing as a result of invoking Server.Execute() or Server.Transfer().

Lets see an example:

You hit a URL http://www.mysite.com/pages/home.aspx and called Server.Execute(“~/pages/common/CheckStatus.aspx”) or Server.Transfer(“~/pages/common/CheckStatus.aspx”) within the Home.aspx

Now, the Request.CurrentExecutionFilePath within the CheckStatus.aspx would return /pages/common/checkstatus.aspx (The page that is being executed from the Home.aspx)

And, Request.FilePath within the CheckStatus.aspx would return /Pages/Home.aspx (The original page that executed the Server.Execute() or Server.Transfer()

Request.Path

This returns the Request.FilePath, including any query string parameters if present.

Request.PhysicalApplicationPath

This returns the web application’s physical folder location, no matter whether it is a Site or Virtual directory. For example, C:\applications\aspnet\www.mysite.com\

Request.PhysicalPath

This returns the physical path of the currently requested file in the URL. For example, C:\applications\aspnet\www.mysite.com\home.aspx

To summarize:

  • If any user control has an HTML element or Asp.net server control that has any kind of path property (src or href or ImageUrl), the path value should be started with the "~/" (Tilde) and the element should be converted to a Server control (By adding runat=”server”).

  • If any page has an HTML element that has any kind of path property, the path values should be started with a relative path "/" or "../" (Given that, the correct relative path is set). And, for any server control in the pages, the property values should be started with the "~/"(Tilde).

  • If path values if to be set in the CodeBehind files, the control.ResolveUrl(“~/path”) has to be used. If path values are to be set inside an HttpHandler, or, HttpModule, or, a class library, the VirtualPathUtility.ToAbsolute(“~/path”) has to be used.

Isn’t it wonderful to have some clear understanding on the basics? I wish I had that in my early days!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here