ASP.NET Web API tries to serve physical directories instead of mapped controllers

Viewed 1289

I have an existing ASP.NET Web API 2 project that was previously served under route prefix /api/ (example.com/api/Users), but now it needs to be moved directly under the root (example.com/Users).

The problem is that now some of the routes match physical directories in the project root, and IIS tries to serve those directories when making such conflicting request.

This is what the project structure looks like:

MyApi/
  App_Start/
  Controllers/
  Models/
  ...
  Permissions/
  ...
  Global.asax
  Global.asax.cs
  ...
  Web.config

The routes are mapped using attribute routes: config.MapHttpAttributeRoutes();

And here's a simplified sample of a problematic controller:

namespace MyApi.Controllers
{
    [RoutePrefix("Permissions")]
    public class PermissionController
    {
        [Route("")]
        [HttpGet]
        [ResponseType(typeof(IEnumerable<Permission>))]
        public IHttpActionResult Get()
        {
            return ...

When a request is made to example.com/Permissions, IIS first responds with a 301 redirect to example.com/Permissions/ (trailing slash) and then with:

IIS 10.0 Detailed Error
HTTP Error 403.14 - Forbidden
The Web server is configured to not list the contents of this directory.

Detailed Error Information:
Module           DirectoryListingModule
Notification     ExecuteRequestHandler
Handler          StaticFile
Error Code       0x00000000

All other controllers without conflicting directory names work as expected.

I've tried both enabling and disabling runAllManagedModulesForAllRequests, I've tried adding and removing handlers like this:

<add name="ApiURIs-ISAPI-Integrated-4.0" path="*" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" preCondition="integratedMode,runtimeVersionv4.0" />

And I've even tried removing the StaticFile handler, but nothing I've tried so far has worked.

This is the Web.config currectly. It is identical to what Visual Studio 2015 generates by default when creating a new empty Web API 2 project:

...
  <system.webServer>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
      <remove name="OPTIONSVerbHandler"/>
      <remove name="TRACEVerbHandler"/>
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
    </handlers>
  </system.webServer>
...

So can I prevent IIS from trying to serve physical files inside the project? The API should only serve dynamically generated content from the controllers.

1 Answers

After a lot of trial and error, I seem to have found the solution.

This flag makes the API prefer the controller instead of the directory:

System.Web.Routing.RouteTable.Routes.RouteExistingFiles = true;

Changes for Web.config:

  • runAllManagedModulesForAllRequests allows dots in the URL (/Users/firstname.lastmane)
  • Removing the StaticFile handler will return 404 errors instead of 403 erros for physical directories which don't match any mapped routes.

Content:

...
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <remove name="StaticFile" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
...
Related