Missing Css Files, Exceptions, and the VirtualPathProvider

Seriously, I am going to talk about stuff other than the Virtual Path Provider sometime...:)

So I noticed an oddity yesterday that I just had to track down. I had the path wrong on a css file reference of mine and this was causing an exception to be thrown. Odd, you say? Well, I thought so too. So here is how it actually happened.

When I added some VPP support (locally, not on the web yet) for my blog, I noticed that an unhandled exception was being thrown when I visited certain pages. I had never seen it before; it didn't directly affect user experience at all. I just happened to notice it because I log errors in Global.asax's Application_Error. So I looked at it and I noticed I had the path wrong on a rather inconsequential css file. So I thought to myself, "Why would an invalid css file path be throwing an error." So I followed the stack trace down on the error, and with Reflector, found the answer.

Surprisingly, it was related to the fact that I was now using a VPP. Down in the bowels of ASP.NET there is this StaticFileHandler class. It has a method called "ProcessRequestInternal". This method was called for the request for my css file. If you have a VPP registered, it takes a different path than otherwise. It checks to see if the VPP registered as the first VPP for the hosting environment is the MapPathBasedVirtualPathProvider, and if it isn't, it calls a method called RespondUsingVirtualFile.

Now this VPP thing works like the chain of responsibility pattern. Let's say you register a VPP called "FooVPP". The HostingEnvironment takes it current VPP, the MapPathBasedVirtualPathProvider, assigns it to the "Previous" property of FooVPP and sets FooVPP as its current VPP. If you register another VPP called "BarVPP", that one becomes the current, its previous is set to FooVPP, and its previous is still MapPathBasedVirtualPathProvider. So any request gets handled by BarVPP, and if it can't handle it, it hands it to FooVPP, who handles it if it can. If it can't, in then handles it to the MapPathBasedVirtualPathProvider and it tries to handle it. If it can't, nothing can, and you get a 404.

So, back to StaticFileHandler. It checks HostingEnvironment.UsingMapPathBasedVirtualPathProvider method, which will return false, because it checks to see if the top level VPP is the MapPathBasedVirtualPathProvider. In this  case it is not, so it returns false. Because of that it called RespondUsingVirtualFile (in StaticFileHandler).

In RespondUsingVirtualFile it first creates a variable for holding an instance of VirtualFile, and it is of course null. Next it checks the whole chain of VPP's to see if the file exists. Because the path was wrong on the css file, the check comes back false. And therein lies the problem. The VirtualFile instance is only set if a file is found (which of course makes sense). If it doesn't find the file, then it throws a "File does not exist" exception, and then you have the end of this process.

So here's the takeaway lessons on this:

1. Don't have inconsequential css files.

2. Spell your paths right.

3. Be sure to pay attention to exceptions thrown in Application_Error. Don't throw any more exceptions than you need to. 


David Nelson (5/1/2007 9:17 PM)

I would think it should be able to check if the file exists and if not simply return a 404 error, just like would happen without VPP. Throwing an exception for a missing css file sounds like pretty bad design...

Eric (5/1/2007 10:05 PM)

That would make more sense to me as well. I wonder what they were thinking. Actually, it wouldn't surprise me if they missed that one.