Introduction
No, there’s no flying pigs, and Satan isn’t wearing a woolly hat, but this is a post about WebAPI (boooo!) I hit an “interesting” issue today, and felt a blog post was in order in case anyone else is bitten by this rather annoying “feature”.
I added a very simple route definition:
config.Routes.MapHttpRoute(
name: "foo",
routeTemplate: "{controller}/{name}",
defaults: new { name = RouteParameter.Optional },
constraints: new { controller = "foos" });
And a very simple controller:
namespace Bar
{
using System.Web.Http;
using Bar.Hypermedia;
public class FoosController : ApiController
{
private readonly IFooService fooService;
public FoosController(IFooService fooService)
{
this.fooService = fooService;
}
[HttpGet]
public IEnumerable Root()
{
return this.fooService.GetFoos();
}
[HttpGet]
public Foo Root(string name)
{
return this.fooService.GetFoo(name);
}
}
}
It couldn’t get much more simple – just a controller that returns a collection of Foos, or a single Foo, depending on whether the name of the Foo was specified. I fired up curl and getting a single Foo was fine, but browsing to /foos/ came back with:
HTTP Error 403.14 – Forbidden
The Web server is configured to not list the contents of this directory.
Err.. what?!
The Solution
The solution was easy – just ask a friendly neighbourhood ASP.Net WebAPI MVP to fix it for me 😉 Filip stepped up and confirmed all my code was fine, but after some head scratching noticed that I had my FoosController.cs inside a folder called Foos, and renaming that folder made the route work fine. It seems that if IIS/Asp.Net sees a folder that exactly matches the URL you’re asking for (/foos in this case) then it takes over and ignores any routes you have setup. Renaming folders would be a major pain in the backside, but luckily you can disable this behaviour by adding one line to the global.asax:
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
// Stop IIS/Asp.Net breaking our routes
RouteTable.Routes.RouteExistingFiles = true;
WebApiConfig.Register(GlobalConfiguration.Configuration);
}
}
So many thanks to Filip for helping me solve this one – I’m sure I’d have lost time, and hair, trying to figure it out on my own. I’d consider this behaviour to be a bug, but I’m sure it’s actually a feature in someone’s mind. If only there was a better way to write HTTP APIs in .net…. 😉


