7

I want to enable CORS on one specific action in an Asp.net Web Api. Here's how I'm trying to do it:

[Route("api/mycontroller/myaction")]
[HttpPost]
[EnableCors("https://example.com", "*", "post")]
public async Task<IHttpActionResult> MyAction()
{
    ...
}

But when I send an OPTIONS request to the route, I get back an error: "The requested resource does not support http method 'OPTIONS'." I also tried removing the [HttpPost] annotation to no avail. What am I missing?

Shlomo Zalman Heigh
  • 3,968
  • 8
  • 41
  • 71
  • hmmm, have you tried just sending the request as a POST and not OPTIONS? I think when you make a CORS request, the browser does the OPTIONS stuff for you in the background (I could be mistaken though). – victor Aug 10 '17 at 20:02
  • The POST request itself works, but the browser first sends an OPTIONS request and that fails, so it never ends up sending the POST. – Shlomo Zalman Heigh Aug 10 '17 at 20:07
  • [EnableCors("https://example.com", "*", "post, options")]? Tell the action to accept OPTIONS requests as well as POST – victor Aug 10 '17 at 20:10
  • Same. If I add the [HttpOptions] annotation along with [HttpPost], I don't get an error but then it tries to execute the action method and wants authorization. EnableCors is supposed to respond to OPTIONS requests. – Shlomo Zalman Heigh Aug 10 '17 at 20:15

3 Answers3

7

For me, I added the following headers to the request by adding the following code to the Application_BeginRequest function of the Global.asax.cs file:

protected void Application_BeginRequest()
{
    if (Request.Headers.AllKeys.Contains("Origin", StringComparer.CurrentCultureIgnoreCase)
        && Request.HttpMethod == "OPTIONS")
    {
        Response.AddHeader("Access-Control-Allow-Headers", "content-type", "accept", "pragma", "cache-control", "authorization");
        Response.End();
    }
}

I have little idea why this works. Out of curiosity, I tried adding all headers by using an asterisk but then Web API complained that the Authorization header was missing.

J Weezy
  • 3,507
  • 3
  • 32
  • 88
Ciaran Gallagher
  • 3,895
  • 9
  • 53
  • 97
  • you saved my life :) also i need to add the code to web.config file inside the system.weserver tag ` ` –  Oct 16 '19 at 10:34
5

You've probably missed the higher level call to HttpConfiguration.EnableCors, as described here: https://enable-cors.org/server_aspnet.html.

Add this code to your configuration:

public static void Register(HttpConfiguration config)
{
    // New code
    config.EnableCors();
}
Kirk Larkin
  • 84,915
  • 16
  • 214
  • 203
  • I thought that calling `config.EnableCors()` in WebApiConfig.cs would enable CORS for the whole API. I didn't realize that it is a prerequisite for using the annotations. I also did have OWIN CORS installed, which I removed as per https://stackoverflow.com/a/29452419/560722 – Shlomo Zalman Heigh Aug 10 '17 at 21:28
5

To ensure the OPTIONS request gets handled by your application code and not some other part of the system before it reaches your app code, you may try adding the following to your web.config:

<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>

You might also need to include:

<add name="OPTIONSVerbHandler" path="*" verb="OPTIONS"
  modules="IsapiModule" requireAccess="None"
  scriptProcessor="C:\Windows\System32\inetsrv\asp.dll"
  resourceType="Unspecified" />

See the answer at IIS hijacks CORS Preflight OPTIONS request.

Or maybe even just this:

 <add name="OPTIONSVerbHandler" path="*" verb="OPTIONS"
   modules="ProtocolSupportModule" requireAccess="None" />

If none of that on its own works, then in your global.asax or other code you might try:

if (filterContext.HttpContext.Request.HttpMethod == "OPTIONS")
{
    filterContext.HttpContext.Response.Flush();
}

…or some other variation on that, for example:

if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OridinalIgnoreCase)
    && Request.HttpMethod == "OPTIONS") {
    Response.Flush();
}

Regardless of what specific code you use to do it, the point is to:

  • make sure OPTIONS requests are actually getting caught/handled by your application code—not caught/handled by some other part of the system before ever reaching your app code
  • make sure you have explicit handling for OPTIONS requests in your application code
  • make the OPTIONS handling in your application code just do Response.Flush()

Or another approach I’m not sure is relevant to your situation as coded but I’ll mention just in case:

public HttpResponseMessage Options()
{
    var response = new HttpResponseMessage
    {
        StatusCode = HttpStatusCode.OK
    };
    return response;
}
sideshowbarker
  • 81,827
  • 26
  • 193
  • 197