Fixing Relative Paths in C# ASP.NET When Using Url Rewriting
UPDATE: This method is generally used in situations where you have relative paths in your project so that links (image, href, css, etc) work on your development environment (where the VS web server inserts a virtual path), as well as your production environment that may or may not have that same virtual path. The simpler way to handle this situation is to change the virtual path in your development environment. To do this, click on your web site project and look in the properties dialog. You should see a Virtual Path setting. Change that to '/' and you will no longer get a virtual path in your development environment. This only works in VS 2005 SP1 and later. Once you have the project configured this way, you are safe to use absolute paths everywhere in your project.
One of our clients recently asked us to add URL rewriting to their site so that the URLs looked nicer. Essentially, we were trying to change something like http://devtoolshed.com/product.aspx?id=1234 to something like http://devtoolshed.com/product/1234. You can accomplish this very easily using the URL Rewrite Module for IIS 7, which is very cool and will probably be the topic of a future blog post.
Everything was working well until I realized that this broke any links or references that were relative instead absolute. For example, consider an image reference to "images/logo.gif". After URL Rewriting, since the base url had changed, the browser was now looking for http://devtoolshed.com/product/images/logo.gif, which doesn't exist.
There are a number of different ways people attempt to solve this problem - most of which don't work in all situations.
Option 1: Make all paths absolute.
One option is to change all URLs to absolute paths, like "/images/logo.png". The problem with this is that it doesn't work if you're viewing the web site from the visual studio devlopment environment because the development web server launches the site from a sub-directory instead of from the "web server" root. You can go this route, but you'll have trouble developing and testing.
Option 2: Use ~/.
.NET controls allow you to set urls to "~/page.aspx" or "~/images/image.png", and it will handle changing ~/ to the correct path for you. Magic. This works great in both development environments and production environments in normal situations, but it still doesn't work when you use URL Rewriting because it translates into relative paths still.
Option 3: Use more URL Rewriting rules to fix things
You could tell the URL rewriting engine to intercept these newly broken references and rewrite them into URLs that work. In desparation I went down this path a few steps - it was a nightmare. I wouldn't recommend it.
Option 4: Using Request.ApplicationPath (the right way).
Prefixing all of your links with Request.ApplicationPath works well for the most part. It gives you the absolute path to the root of your web site, which is good because we want to work with absolute paths, and it's good because it changes based on whether you're running in your development environment or on a production server. It's one failing is that it is inconsisant when it comes to trailing slashes. Without going into details about why this is, I'll just tell you that the correct way to address this is to use Request.ApplicationPath.TrimEnd('/') whenever you use it.
What you end up with here is code that looks like this in your code behind files:
Response.Redirect(Request.ApplicationPath.TrimEnd('/') + "/url.aspx");
sb.Append("<a href=\"" + Request.ApplicationPath.TrimEnd('/') + "search.aspx?q=" + ...);
and like this in your aspx pages:
<a href=<%= Request.ApplicationPath.TrimEnd('/') + "/Rss.aspx" %> >RSS Feed</a>
<link href=<%= Request.ApplicationPath.TrimEnd('/') + "/css/style.css" %> rel="stylesheet" type="text/css" />
And of course, what you really end up with are links that always work.
What about CSS?
CSS links are a little more trickey because you can't use Request.ApplicationPath in them by default. The solution - make it so that css files are parsed for C# code. I cringed a little when I first saw this technique, but after thinking about it more, and after actually using it, I liked it a lot. Aside from allowing you to create links in CSS files that always work, you have a little more flexibility in the css that is produced - think if/else blocks, etc. Here's how you do it. In your web.config file, add these two sections:
<!-- So we can map paths correctly in css files -->
<add extension=".css" type="System.Web.Compilation.PageBuildProvider" />
<add path="*.css" verb="GET" type="System.Web.UI.PageHandlerFactory" validate="True" />
<add name="Parse CSS Files" preCondition="integratedMode" verb="GET" path="*.css" type="System.Web.UI.PageHandlerFactory" />
Then in your css files, you can do this:
<!-- This line goes at the top. This makes all browsers recognize this as css -->
<%@ ContentType="text/css" %>
background-image:url(<%= Request.ApplicationPath.TrimEnd("/") %>/images/bg_home_search.png);
8 hours 4 min ago
8 hours 5 min ago
1 week 21 hours ago
- Clustered and nonclustered index architecture:
2 weeks 3 days ago
2 weeks 5 days ago
- This is a decent tips
3 weeks 1 day ago
- I thought it was going to be
3 weeks 1 day ago
- I've just decided to create a
3 weeks 1 day ago
- You have made an
3 weeks 5 days ago
- If you ?re going for ost
3 weeks 5 days ago
- Global.asax Events in IIS 6 and IIS 7 for Static Resources
- Getting Started with .NET Entity Framework
- Changing the Application Pool Identity with Windows Server 2008 and IIS 7
- How to Highlight the Day in the ASP.NET Calendar Control with the SelectedDate Property
- Building a Web Service in ASP.NET 3.5