As a consultant who has had his hands in ASP.NET, I’ve found plenty to cheer about in the new language. But what I find most welcome is that Microsoft finally made Web applications portable. What do I mean by that? By way of example, I'll show how I used this feature during a recent engagement.
I was developing a small application in ASP.NET to handle e-commerce integration for a software development firm. Actually, it was more like a subapplication. This piece was meant to integrate into an existing Web site, allowing me to slowly replace the functionality without incurring the full conversion cost all at once. But I had a small problem: I didn’t know how many folders deep in the Web site—/subapp, /folder/subapp, /folder1/folder2/subapp—the application would end up.
Typically, that would mean that I’d have to code my application using nothing but relative URLs. At first glance that may seem okay, but when you dig in, it poses a slew of problems:
- Storing images in an images subfolder of your application
- Referencing a common style sheet in the root of your application
- Including a file with common code as needed
- Linking to major sections of your application
Let’s say we have an application with multiple folder levels. Some of the HTML might look like this:
<link href=”../../../core.css” type=”text/css” rel=”stylesheet”/>
<a href=”../../products”><img src=”../../i/tr/prod.gif”/></a>
Writing this code is less than exciting, especially because we always to worry about the indirection level. Further, if we copy and paste a fragment of HTML or a whole file, we have to remember to modify the references. None of this is fun, and it increases the complexity of the application as well as the maintenance and management.
Of course, if we knew that the application was going to live at the root folder of a Web server, then the URLs would all get pretty simple. But we’re not so lucky in this case, so we have to be smart.
The solution: The ~ operator
Luckily for us, the kind folks at Microsoft added the ~ operator to ASP.NET. We’ve all seen the operator show up in URLs such as http://www.website.com/~username. This is a throwback to the UNIX days, when the ~ operator was used to designate a user’s home directory. An individual user could then use a path of the form ~/.cshrc to refer to the c-shell resource descriptor file in their home folder. I suspect the folks at Microsoft decided to stick with the standard when they implemented the operator in ASP.NET.
How does it work?
The ~ operator works exactly the same way it did in the UNIX world. You can use it to refer to the root folder of an application in paths—with a few exceptions. First of all, remember that the client browser does not understand the operator, so that means that only tags that run at the server will interpret this operator. Second, not every tag supports this syntax, which makes life a little more confusing.
Let’s look at the example above and see how we might convert it. First, we have to convert the tags to run at the server. We do that by adding
to the tags. Second, we modify the path using the ~ operator. The converted syntax looks like this:
<link runat=”server” href=”~/core.css”
<a runat=”server” href=”~/products”>
<img runat=”server” src=”~/i/tr/prod.gif”/>
I remember the first time I wrote this code. I was so excited because everything looked like it should work. The only problem was that it didn’t. Why? Unfortunately, it looks like Microsoft’s ASP.NET team missed a few tags when they parsed this URL syntax. Specifically, the link tag does not respond well to the ~ operator, which means your pages won’t look the way you expect them to look. Well, at least the links and the images work just fine. So, what do we do?
Going the extra mile
I spent a little while figuring out the right way to solve this problem. My first inclination was to use a server-side include to handle the style sheet. But that leads to other problems. For one thing, the pages are larger than they need to be for download. In addition, the pages don’t get the benefit of client-side caching of the CSS file. Clearly there has to be a better answer.
The solution is to embed a function call where the ~ operator won’t work. For my part, I created a Utility class and gave it the following function:
public static string Home(HttpRequest req, string str)
string vroot = req.ApplicationPath;
vroot = vroot.TrimEnd(‘/’);
string output = vroot + str;
The function code itself is pretty straightforward. We pull the application’s path from the Request object, trim the trailing slash (if available), and then append the path passed in. An HTML call to it would look like this:
Now when the page is sent to the client, the right path will be placed in the link statement. The only thing that’s curious here is the fact that we’re passing the Request object into the function. Unfortunately, only the page itself has access to the Request object, which is needed by the function to access the application’s path. So, to call into it, we have to pass it in.
It is completely reasonable to call Request.ApplicationPath directly in the ASP.NET code. The only problem I've found is that applications residing at a Web root do not receive a trailing slash, while those residing in a subdirectory do. So there’s the need for a TrimEnd call to make sure there’s never a trailing slash. I also find the call to a function to be easier to read. But, if you wanted to embed the code inline, you would call it like this:
I prefer using the function because it makes the code a bit easier to read. But both methods are perfectly valid.
An extra bonus
Coding your applications in this manner gives you one other value-add—you can actually develop multiple applications under Windows 2000 Professional or Windows XP Professional.
If you’re like me, you have multiple development machines. One of mine is a laptop running Windows XP Professional. The problem is that IIS under Windows XP Professional limits you to a single Web server at a time. In turn, this means that every application must live in a subfolder of the Web server, such as http://localhost/application. However, several of my applications are root-level Web sites, such as http://www.application.com.
By developing my applications with the ~ syntax (and its sister function), I can work on my laptop and know that when I deploy the application everything will work.
The bottom line
Using the ~ operator and a little bit of code, you can abstract out the root path of your application from your code. This will allow your customers to roll out the application to a variety of deployments while minimizing the cost of management and maintenance.