ASP.NET Web Application Without .ASPX Extension

January 20, 2006 - 5 minute read -
IIS WebDAV

The Problem

Ok, let's say you want to create an application in ASP.NET. That application should be able to execute .NET code regardless of the path that is requested. You don't want to map the .NET execution to a specific extension and you want handle all requests in a simple manner. Well, I think I figured out how.

In the Java Servlet world, you can easily map arbitrary paths to a servlet handler, such that all request to '/somedirectory' are handled by your 'SomeDirectoryServlet'. Ruby on Rails uses a controller as well and you can do URL rewriting with that controller very easily to support arbitrary path schemes. But in the .NET/IIS world, all of the configuration for handling is based solely on file extensions.

An Actual Project That Needs This

At SpiderLogic (where I work), we are working on a client project where their current infrastructure is all .NET. Their current application is entirely web based. It allows users to (among other things) upload files and share them among a group of users. Now they want to allow users to access files on a Windows desktop and interact with them in a much more natural way, by dragging and dropping, etc. Windows Explorer has the ability to mount WebDAV paths the same as it can with windows file sharing. So, basically we wanted to see if we could build a virtual WebDAV server that would allow us to read file meta-data from a Database and then expose that over the web as a WebDAV share.

WebDAV is an extension to HTTP that allows for Distributed Authoring and Versioning. Basically it looks a lot like HTTP, where it has verbs and requests files.

A WebDAV header might look something like:

PROPFIND /some/path/ HOST www.example.com

The Solution

As I said above, IIS exclusively uses file extensions to map files to code to execute the file. .NET provides HTTP Handlers and HTTP Modules that allow you to handle requests that should not be handled by a traditional Page. A Module registers itself when it is loaded to respond to specific events in the HTTP Request life-cycle. Sometimes this is called an Intercepting Filter. Modules listen to events and can respond to that event. They can do many things, check security, add content to the response, transform an XML response into an appropriate form based on the calling device, etc. They can also take over the entire request-response and handle it in its entirety.

For our example, we want to respond to a very early event - BeginRequest and fulfill the request ourselves.

using System;
using System.Web;</p>
<p>public class DavModule : IHttpModule {</p>
<p>    private HttpApplication context = null;</p>
<p>    public void Dispose() {
        context.BeginRequest -= new EventHandler(context_BeginRequest);
    }</p>
<p>    public void Init(HttpApplication context) {
        this.context = context;
        this.context.BeginRequest += new EventHandler(context_BeginRequest);
    }</p>
<p>    void context_BeginRequest(object sender, EventArgs e) {
        HttpApplication app = (HttpApplication) sender;
        app.Response.ContentType = "text/plain";</p>
<p>        if (app.Request.Path.EndsWith("Help"))
            app.Server.Transfer("Help.aspx");
        else {
            app.Response.Write("Path: " + app.Request.Path);
            app.Response.End();
        }
    }
}

The IHttpModule responds to begin request events and takes over handling the fulfillment of the HTTP response. The example demonstrates that you can get the request path and then parse it to handle different cases. One use would be for doing internal URL rewriting (although I suspect that this kind of URL rewriting would break the post-back of .aspx files). As I said at the start, we plan on trying to implement a virtual directory using WebDAV. The possibilities are limited only by your imagination.

The handler itself is made available to a web application by registering it in the Web.config file for the application.

Web.config configuration:

    <system.web>
      <httpModules>
        <add name="DavModule" type="DavModule"/>
      </httpModules>
    </system.web>

Of course if that was all there was too it, this would be a much less interesting article. The trick to making this all work is to properly configure IIS. We want to handle all requests to our application using the .NET code that we have written. We don't want IIS to return 404s (Not Found) for files that don't exist since we want to implement a dynamic, virtual directory and not just deal with files on the file system.

To do this, you have to remove all of the existing handlers under the Application Configuration. Then you want to map all extensions (.*) and in our case all Verbs (GET, POST, PROPFIND, MKCOL - which allows us to hopefully respond to WebDAV verbs as well) to the aspnet_isapi.dll. Overview of ISS configuration. Here's the editing screen that you will see: Details of ISS configuration.

This was Step 1 of the proof of concept to see if we can make the WebDAV system work. Hopefully a future posting will be about the success of the implementation.

This is a very non-obvious thing to do in IIS/ASP.NET, but for some applications it is a necessary thing. Of course if you have more control over the tools and language you use to implement it, then maybe you could choose something that was better suited for this application. Maybe someone else will find this useful? What do you think?

Thanks to Brennan and Oliver who work at SpiderLogic for their help with this.

Update Under Windows Server 2003: .* is not allowed in the mapping , there is an extra ‘all extensions’ section where you can add the aspnet_isapi.dll.