Search code examples
c#asp.net-web-apicorsasp.net-mvc-areaspreflight

Preflight request returns 404 Not Found for Controllers in an Area


I have a WebAPI project, and I have enabled CORS by following these steps:

  1. Get the nuget package: Install-Package Microsoft.AspNet.WebApi.Cors.
  2. Added config.EnableCors(); to WebApiConfig.cs.
  3. Added [EnableCors(origins: "*", headers: "*", methods: "*")] to my controller.

I have controllers that are in the root Controllers folder, and within an Area.

/Controllers/ValuesController
/Areas/TestArea/Controllers/OtherStuffController

Both controllers have the [EnableCors(...)] attribute.

My problem is that only ValuesController works.

Using Fiddler to inspect the requests for the OtherStuffController I can see that the client sends a preflight OPTIONS request, but the server responses with a 404 not found instead of a 200 OK, yet the ValuesController works perfectly.

What am I doing wrong? Why doesn't the controller in the TestArea area work with CORS requests, yet the Values controller does?

There is nothing different about the two controllers other than the name, and that it's in an Area.

ADDITIONAL INFO

Routes:

// In WebApiConfig.cs
config.Routes.MapHttpRoute("DefaultApi", "{controller}/{id}", 
    new {id = RouteParameter.Optional} );

// In Areas/TestArea/TestAreaAreaRegistration.cs
context.MapRoute("TestArea_default","TestArea/{controller}/{id}",
    new {id = UrlParameter.Optional});

URLs I'm using to access the action methods:

http://localhxst:57578/values
http://localhxst:57578/testarea/otherstuff

Values request (working):

(REQUEST)
OPTIONS http://localhost:57578/values HTTP/1.1
Host: localhost:57578
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://localhxst:12345
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Connection: keep-alive

(RESPONSE)
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: authorization
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcYmVuLmdvbGRlblxEb2N1bWVudHNcV29ya1xWb2ljZVNpbXBsaWZpZWRcQVBJXFZvaWNlU2ltcGxpZmllZC5QdWJsaWNBUElcdmFsdWVz?=
X-Powered-By: ASP.NET
Date: Thu, 24 Jul 2014 20:12:56 GMT
Content-Length: 0

Followed by:

(REQUEST)
GET http://localhxst:57578/values HTTP/1.1
Host: localhost:57578
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0
Accept: application/json
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Bearer HQkpyzeQ5NM1Va1Ow__6N6JzuRdMweDIJDneDQer1tL1uMhkrG4gsRYkXLQ1F4782L5vDTMOkoqvOEtO753n6TJ2BU-KNaxdXRAtf336c-r8MXMi_nWliw1vT1Xa7Wmt3eV5b9HmJR4Bnmt4gTavtoC0qwQVsoX_miV_VanJ98j_aaoNdNNZcnN5FsJ2eoLx7UebBDxXBMFmXEtOUTtWCsRp-g26mwKjbK3HeDoiUU2Ivh-VleUVImdh9ASwInbZ
Referer: http://localhxst:12345/
Origin: http://localhxst:12345
Connection: keep-alive    

(RESPONSE)
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: *
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcYmVuLmdvbGRlblxEb2N1bWVudHNcV29ya1xWb2ljZVNpbXBsaWZpZWRcQVBJXFZvaWNlU2ltcGxpZmllZC5QdWJsaWNBUElcdmFsdWVz?=
X-Powered-By: ASP.NET
Date: Thu, 24 Jul 2014 20:12:56 GMT
Content-Length: 13

["ABC","DEF"]

OtherStuff request (prelight failed):

(REQUEST)
OPTIONS http://localhxst:57578/testarea/otherstuff HTTP/1.1
Host: localhost:57578
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://localhost:12345
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Connection: keep-alive

(RESPONSE)
HTTP/1.1 404 Not Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcYmVuLmdvbGRlblxEb2N1bWVudHNcV29ya1xWb2ljZVNpbXBsaWZpZWRcQVBJXFZvaWNlU2ltcGxpZmllZC5QdWJsaWNBUElcdGVzdGFyZWFcb3RoZXJzdHVmZg==?=
X-Powered-By: ASP.NET
Date: Thu, 24 Jul 2014 20:35:08 GMT
Content-Length: 3194
<!DOCTYPE html>
<html>
    <head>
        <title>The resource cannot be found.</title>
        <meta name="viewport" content="width=device-width" />
        <style>
         body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;} 
         p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
         b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
         H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
         H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
         pre {font-family:"Consolas","Lucida Console",Monospace;font-size:11pt;margin:0;padding:0.5em;line-height:14pt}
         .marker {font-weight: bold; color: black;text-decoration: none;}
         .version {color: gray;}
         .error {margin-bottom: 10px;}
         .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
         @media screen and (max-width: 639px) {
          pre { width: 440px; overflow: auto; white-space: pre-wrap; word-wrap: break-word; }
         }
         @media screen and (max-width: 479px) {
          pre { width: 280px; }
         }
        </style>
    </head>

    <body bgcolor="white">

            <span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>

            <h2> <i>The resource cannot be found.</i> </h2></span>

            <font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">

            <b> Description: </b>HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. &nbsp;Please review the following URL and make sure that it is spelled correctly.
            <br><br>

            <b> Requested URL: </b>/testarea/otherstuff<br><br>

            <hr width=100% size=1 color=silver>

            <b>Version Information:</b>&nbsp;Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.34009

            </font>

    </body>
</html>
<!-- 
[HttpException]: The controller for path &#39;/testarea/otherstuff&#39; was not found or does not implement IController.
   at System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType)
   at System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName)
   at System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory)
   at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
   at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
-->

ValuesController.cs

namespace MyAPIApp.PublicAPI.Controllers
{

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [Authorize]
    public class ValuesController : ApiController
    {
        private static readonly List<string> values = new List<string> { "ABC", "DEF" };

        public IEnumerable<string> Get() { return values; }
    }
}

OtherStuffController.cs

namespace MyAPIApp.PublicAPI.Areas.TestArea.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [Authorize]
    public class OtherStuffController : ApiController
    {
        private static readonly List<string> values = new List<string> { "ABC", "DEF" };

        public IEnumerable<string> Get() { return values; }
    }
}

Solution

  • I have found the problem.

    When you create an Area in the project via the Add > Area context menu, Visual Studio creates the area with an AreaRegistration template using the following code:

    public override void RegisterArea(AreaRegistrationContext context) {
        context.MapRoute(
            "TestArea_default",
            "TestArea/{controller}/{id}",
            new {id = UrlParameter.Optional}
            );
    }
    

    However, this code is not correct for ApiControllers. The correct route registration code is:

    public override void RegisterArea(AreaRegistrationContext context) {
    
        context.Routes.MapHttpRoute(
            "TestArea_default", 
            "TestArea/{controller}/{id}", 
            new { id = RouteParameter.Optional });
    }
    

    It turns out it was nothing to do with CORS.