This post describes briefly how I implemented url rewriting with the UrlRewriting.Net
package.
My objectives were simple, to:
- Use simple readable urls wherever possible e.g. http://domain.com/app
- Honour querystring parameters passed to pages e.g. http://domain.com/app?param=value
- Map several Urls to one implementation page e.g. http://domain.com/red
=> http://domain.com/colours.aspx?name=red
- Not have to create physical folders for all virtual folders e.g. /red does not actually
exist on disk!
- Ensure all ASP.Net functionality remains intact e.g. postbacks,
themes, authentication, AJAX
- Must work for apps rooted at the domain e.g. http://domain.com/ and in
subdirectories e.g. http://localhost/devapp/.
This was based on the following setup:
- An IIS 5.5/6 web server
- IIS wilcard (.*) request mapping
- UrlRewriting.Net package
- FormRewriter control adapter (see
ScottGu's article
here)
- An existing large site with default pages named default.aspx
I chose UrlRewriting.Net as it seemed fullest featured package out there,
and the only one I tested that would work correctly with FormsAuthentication
and ASP.Net AJAX.
With this running 'out of the box' I found a couple of problems.
Issue 1: Trailing slashes
This can be an issue with themes and is also required to get default pages working
sensibly. The idea is to remove the trailing slash on urls except those on the authority.
For example, we want:
- http://domain.com/ to map to http://domain.com/ i.e. no change
- but http://domain.com/dir/ to map to http://domain.com/dir i.e.
slash removed
My solution is based on the one by Fabrice
here but is modified to exclude the authority part of the Url (e.g. http://domain.com/)
so we don't end up in an endless loop of redirects. This rule has to be a redirect since UrlRewriting.Net will not process
multiple rewrites. In any case you don't want users or search engines seeing http://domain.com/dir/
as different from http://domain.com/dir so a permanent redirect to the
slashless url makes good sense here, giving a single identity to multiply addressable
resources.
The rule looks like this:
<add name="RemoveTrailingSlash"
virtualUrl="^~/(.*)/(\?.*)?$"
destinationUrl="~/$1$2"
rewriteUrlParameter="ExcludeFromClientQueryString"
redirectMode="Permanent"
redirect="Application"
ignoreCase="true" />
Issue 2: The Default Page
The default page is set as an attribute in the UrlRewriting configuration
and is a workaround for the default page being lost when the IIS wildcard mapping
is added. However, UrlRewriting default pages do not work as in
IIS - the default page is actually appended to all directory requests.
For example:
http://mydomain.com becomes http://mydomain.com/default.aspx
In projects where branding and SEO are primary considerations this is unlikely to
be acceptable.
Switching off the default page functionality (by removing the defaultPage
configuration attribute) prevents this happening but then we have to add rules for
every single instance in which we would like http://mydomain.com to map to the file
http://mydomain.com/default.aspx .
The workaround here is to add the following rule last in the rewrite configuration:
<add name="Default"
virtualUrl="^([a-zA-Z0-9_\-/]*)(\?.*)?$"
rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="$1/Default.aspx$2"
ignoreCase="true"/>
This has the effect of re-instating default page functionality. Note the expression [a-zA-Z0-9_\-/] does not include the period
(.) character (i.e. to skip mapping requests for actual pages, style sheets or other
resources which usually contain a period) but should include any other characters
that may appear in your url. This rule is designed to only map requests such as
http://domain.com/test to http://domain.com/test/default.aspx
and relies on the prior redirect to remove the trailing slash (except at the authority
level).
NB 1. This rule is by no means exhaustive. Modify it to suit the
naming structure of your site.
NB 2. This rule relies on the trailing slash rule being present
first, otherwise it would map requests such as /test/ to /test//default.aspx
.
Summary
In summary I have two core rules, the trailing slash rule and the default rule.
I also have a number of site specific rules in the middle to map top level directories
to a single template implementation page. My config rules looks something like this:
<add name="RemoveTrailingSlash"
virtualUrl="^~/(.*)/(\?.*)?$"
destinationUrl="~/$1$2"
rewriteUrlParameter="ExcludeFromClientQueryString"
redirectMode="Permanent"
redirect="Application"
ignoreCase="true" />
<!-- Begin app specific rules -->
<!-- Category mapping rule example - does not map querystring -->
<add destinationurl="~/Categories/CategoryHome.aspx?cn=$1"
ignorecase="true" name="CategoryHome" rewriteurlparameter="ExcludeFromClientQueryString"
virtualurl="^~/(Travel|Entertainment|Family|Health|Lifestyle|Care|Food|Fashion|Home)(\?.*)?$">
</add>
<!-- End app specific rules -->
<add name="Default"
virtualUrl="^([a-zA-Z0-9_\-/]*)(\?.*)?$"
rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="$1/Default.aspx$2"
ignoreCase="true"/>
Conclusion
Whilst UrlRewriting.Net has it's limitations it can be used to
create a maintainable url rewritten site relatively
quickly and cleanly .
Disclaimer: Incorrect use of Url rewriting can have catastrophic
consequences for your site. One miswritten rule can bring your whole site down.
Use with caution! :)