ASP.NET MVC 2 throws exception for ‘favicon.ico’

I must be on fire or something – third blog in 2 days… awesome!

Turns out this is an issue only on Cassini. Please read Phil Haack's comments below.

Before I begin, in case you’re wondering, favicon.ico is the small image that appears to the left of your web address, once the page loads.

In order to learn more about MVC or any thing for that matter, it’s better to look at the source itself. Since MVC is open source (at least some part of it is), I started looking at the source code that’s available for download. While doing so, I hit Steve Sanderson’s blog site where he explains in great detail the way to debug your app using ASP.NET MVC source code. For those who are not aware, Steve Sanderson’s book - Pro ASP.NET MVC Framework, is one of the best books to learn about MVC.

Alrighty, I followed the article and I hit F5 to debug the default / unchanged MVC project. I put a breakpoint in the DefaultControllerFactory.cs, CreateController() method. To know a little more about this class and the method, read this.

Sure enough, the control stopped at the breakpoint and I hit F5 again and the page rendered just fine. But then what’s this? The breakpoint was hit again, as if something else was being requested. I now hovered my mouse over the ‘controllerName’ parameter and it says – favicon.ico. This by itself was more than enough for me to raise my eye-brows, but what happened next just took the ground below my feet.

Oh, oh, I’m sorry I’m just typing, no code, no image, so here are a couple of screen captures. The first one shows the request for the Home controller; I get ‘Home’ when I hover over the parameter:

screen1

And here’s the one that shows the same for call for ‘favicon.ico’.

screen2

So, I step through the code and when the control reaches line 91 – GetControllerInstance() method, I step in. This is when I had the ‘ground-losing’ experience.

screen3

Wow, an exception is being thrown for this file and that too in RTM. For some reason MVC thinks, this as a controller and tries to run it through the MvcHandler and it hits this snag. So it seems like this will happen for any MVC 2 site and this did not happen for me in the previous version of MVC.

Before I get to how to resolve it, here’s another way of reproducing this exception. Revert back all your changes that you did as mentioned in Steve’s blog above.

Now, add a class to your MVC project and call it say, MyControllerFactory and let this inherit from DefaultControllerFactory class. (Read this for details on the DefaultControllerFactory class is and how it is used in a different context). Add an override for the CreateController() method and for the sake of this blog, just copy the same content from the DefaultControllerFactory class. The last step is to tell your MVC app to use the MyControllerFactory class instead of the default one. To do this, go to your Global.asax.cs file and add line 6 of the snippet below:

   1:  protected void Application_Start()
   2:  {
   3:      AreaRegistration.RegisterAllAreas();
   4:   
   5:      RegisterRoutes(RouteTable.Routes);
   6:      ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());
   7:  }

Now, you’re ready to reproduce the issue. Just F5 the project and when you hit the overridden CreateController() method for the second time, this is what it looks like for me:

screen4

And continuing further gives me the same exception.

screen5

I believe this is something that MS should fix, as not having ‘favicon.ico’ file will be common for most of the applications. So I think the when you create an MVC project, line 6 should be added by default by Visual Studio itself:

   1:  public class MvcApplication : System.Web.HttpApplication
   2:  {
   3:      public static void RegisterRoutes(RouteCollection routes)
   4:      {
   5:          routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
   6:          routes.IgnoreRoute("favicon.ico");
   7:   
   8:          routes.MapRoute(
   9:              "Default", // Route name
  10:              "{controller}/{action}/{id}", // URL with parameters
  11:              new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
  12:          );
  13:      }

There it is, that’s the solution to avoid the exception altogether. I tried this both IE8 and Firefox browsers and was able to successfully reproduce the error. Hope someone will look at this issue and find a fix.

Just before I finish up, I found another ‘bug’, if you want to call it, with Visual Studio 2008. Remember how you could change what browser you want your application to run in by just right clicking on the .aspx file and choosing ‘Browse with…’? Seems like that’s missing when you’re working with an MVC project. In order to test the above bug in the other browser, I had to load a classic ASP.NET project, change the settings and then run my MVC project. Felt kinda ‘icky’, for lack of a better word.

7 Comments

  • Thanks for the reply Phil.
    I do know that the user will not see this. But to confirm from your statement:
    Are you saying that the exception will be thrown (even though internally?) If that's the case, then there is a performance hit that users will have a hard time finding out about don't you think? It would be great if VS comes up with a patch to add this line by default to the global.asax.cs file.

  • Well it also depends on your hosting set up. For example, if you deploy to IIS 6, then by default, no it won't happen because *.ico requests are not forwarded to ASP.NET, so ASP.NET MVC would never even get the request.

    You're running in Cassini in which case *all* requests go to the controller. In production environments with IIS, things are different depending on how you configure IIS.

    Remember, Cassini != IIS and doesn't do a good job of emulating IIS.

  • Brilliant. Thanks a bunch for this.

  • I think the more natural way of fixing it is to put "favicon.ico" image iniside root folder of your website :)

  • I agree, put a favicon.ico file in your web root is a good practice!

  • This doesn't just apply to Cassini. If you're running on IIS7 or IIS7.5 in integrated pipeline mode, or you've added ASP.NET as a wildcard application mapping for IIS6, requests for static files will still go through the ASP.NET pipeline, and you'll still get the 404 exception for files which don't exist.

    I suspect this behaviour was the same with ASP.NET MVC v1.0, and a 404 error is exactly what you would expect if you request a file that doesn't exist.

  • Richard, Good points you've given.

    Arun

Comments have been disabled for this content.