Websites tend to
be organic—they grow and change over time. This can create problems when
other applications link to your application. You need some way of
modifying your website without breaking all the existing links to it.
Remapping URLs
The simplest way to remap a URL is to specify the remapping in your application’s web configuration file.
<configuration>
<system.web>
<urlMappings>
<add
url=”~/Home.aspx”
mappedUrl=”~/Default.aspx”/>
</urlMappings>
</system.web>
</configuration>
The
mappedUrl attribute can contain query strings. However, it cannot
contain wildcards. You can use the <urlMappings> element only when
performing simple page-topage mappings. After you add the web
configuration file to your application, any requests for the Home.aspx
page are modified automatically to requests for the Default.aspx page.
It doesn’t matter whether the Home.aspx page actually exists. If the
Home.aspx page does exist, you can never open the page.
When working with
remapped URLs, you often need to determine the original URL that a user
requested. For example, you might want to display a message that tells
users to update their bookmarks (favorites) to point to the new URL. You
can use the following to determine the current URL:
. Request.RawUrl—Returns the original URL (before being remapped).
. Request.Path—Returns the current URL (after being remapped).
. Request.AppRelativeCurrentExecutionFilePath—Returns the application relative URL (after being remapped).
The last property automatically replaces the name of the web application with a tilde (~) character.
Creating a Custom UrlRemapper Module
The
<urlMappings> configuration element discussed in the previous
section performs a very simple task. It remaps one page to another.
However, you’ll quickly discover that you need to perform more complex
remappings. For example, imagine that you have a database that contains a
table of product categories and a table of products. You want your
website’s users to request a URL that contains a product category and be
able to see matching products. For example, if someone requests the
/Products/Soda.aspx page, you want to display all the products in the
Soda category. If someone requests the /Products/Milk.aspx page, you
want to display all the products in the Milk category.
Using the VirtualPathProvider Class
The
VirtualPathProvider class enables you to abstract the pages in a web
application from the file system. In other words, it enables you to
store your ASP.NET pages any way you please. For example, you can use
the VirtualPathProvider class to store all the pages in your application
in a database. This would be an appropriate choice when you need to
build a CMS. If you store pages in a database, then users can update the
pages easily in an application through an HTML form interface and save
the changes to the database. The sample application at the end of this
book uses the VirtualPathProvider class to store code samples in the
database. Users of the website can execute the code samples
directly from the database.
Limitations of the VirtualPathProvider Class
Unfortunately,
you can’t use the VirtualPathProvider with every type of file. In
particular, the following types of files must always be located on the
file system:
- Global.asax file
- Web.Config files
- App_Data folder
- App_Code folder
- App_GlobalResources folder
- App_LocalResource folders
- Bin folder
Every other type of file is fair game. This includes ASP.NET pages, User Controls, Themes, and Master Pages.
Understanding the VirtualPathProvider Class
The VirtualPathProvider class is a MustInherit (abstract) class. It contains the following methods, which you can override:
- CombineVirtualPaths()—Returns a combined path from two paths.
- DirectoryExists()—Returns true when a directory exists.
- FileExists()—Returns true when a file exists.
- GetCacheDependency()—Returns a cache dependency object that indicates when a file has been changed.
- GetCacheKey()—Returns the key used by the cache dependency.
- GetDirectory()—Returns a VirtualDirectory.
- GetFile()—Returns a VirtualFile.
- GetFileHash()—Returns a hash of the files used by the cache dependency.
- OpenFile()—Returns the contents of a file.
Typically, you
override the FileExists() and GetFile() methods to retrieve a file from
your data store. If you want to represent directories, then you also
need to override the DirectoryExists() and GetDirectory() methods.
Notice that several of these methods are related to caching.
VirtualPathProvider needs to know when a file has been modified so that
it can retrieve the new version of the file and compile it. By default,
the ASP.NET Framework uses a file dependency to determine when
a
file has been modified on the hard drive. However, in this situation a
SqlCacheDependency is used because the files will be stored in a
database.
VirtualPathProvider also includes a very useful property:
- Previous—Returns the previously registered VirtualPathProvider. The Previous property enables you to use the default VirtualPathProvider. For example, if you want to store some files in the file system and other files in the database, then you can use the Previous property to avoid rewriting all of the logic for working with files in the file system.
The GetFile()
method returns an instance of the VirtualFile class. When using the
VirtualPathProvider, you must create a new class that inherits from the
VirtualFile class. This class contains the following properties:
- IsDirectory—Always returns False.
- Name—Returns the name of the file.
- VirtualPath—Returns the virtual path of the file.
The VirtualFile class also contains the following method:
- Open()—Returns the contents of the file.
Typically, when
creating a class that inherits from the VirtualFile class, you override
the Open() method. For example, we’ll override this method to get the
contents of a file from a database table in the code sample built in
this section.
The GetDirectory() method returns an instance of the VirtualDirectory class. This class contains the following properties:
- Children—Returns all the files and directories that are children of the current directory.
- Directories—Returns all the directories that are children of the current directory.
- Files—Returns all the files that are children of the current directory.
- IsDirectory—Always returns True.
- Name—Returns the name of the directory.
- VirtualPath—Returns the virtual path of the directory.
There is another
class in the ASP.NET Framework that you’ll want to use when working with
the VirtualPathProvider class. The VirtualPathUtility class contains
several useful methods for working with virtual paths:
- AppendTrailingSlash()—Returns a path with at most one forward slash appended to the end of the path.
- Combine()—Returns the combination of two virtual paths.
- GetDirectory()—Returns the directory portion of a path.
- GetExtension()—Returns the file extension of a path.
- GetFileName()—Returns the file name from a path.
- IsAbsolute()—Returns True when a path starts with a forward slash.
- IsAppRelative()—Returns True when a path starts with a tilde (~).
- MakeRelative()—Returns a relative path from an application-relative path.
- RemoveTrailingSlash()—Removes trailing slash from the end of a path.
- ToAbsolute()—Returns a path that starts with a forward slash.
- ToAppRelative()—Returns a path that starts with a tilde (~).
By taking advantage of the VirtualPathUtility class, you can avoid doing a lot of tedious string parsing on paths.
Registering a VirtualPathProvider Class
Before
you can use an instance of the VirtualPathProvider class, you must
register it for your application. You can register a VirtualPathProvider
instance with the
HostingEnvironment.RegisterVirtualPathProvider() method.
You need to
register VirtualPathProvider when an application first initializes. You
can do this by creating a shared method named AppInitialize() and
adding the method to any class contained in the App_Code folder. The
AppInitialize() method is automatically called by the ASP.NET Framework
when an application starts.