15

I am making the following request to an asp.net web api PUT method from my angular.js client:

var org = {
                  OrgId: 111,
                  name: 'testing testing'
          };
$http.put("http://localhost:54822/api/data/putorganisation/1/", org).then(function(status) {
         console.log("success PUT");
         return status.data;
});

However getting the following errormsg (in fiddler):

{"message":"The requested resource does not support http method 'OPTIONS'."}

This is a part of my asp.net web api web.config file:

 <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type,x-xsrf-token,X-Requested-With" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
      </customHeaders>
    </httpProtocol>
    <validation validateIntegratedModeConfiguration="false" />
    <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" />
      <remove name="WebDAV" />
    </handlers>
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="WebDAVModule" />
    </modules>
  </system.webServer>

data controller web api:

public HttpResponseMessage Options()
{
            var response = new HttpResponseMessage();
            response.StatusCode = HttpStatusCode.OK;
            return response;
}

public HttpResponseMessage PutOrganisation(int id, [FromBody]Organisation org)
{
  var opStatus = _Repository.UpdateOrganisation(org);
  if (opStatus.Status)
  {
     return Request.CreateResponse<Organisation>(HttpStatusCode.Accepted, org);
  }

return Request.CreateErrorResponse(HttpStatusCode.NotModified, opStatus.ExceptionMessage);
}

This is my question: Why do I get the errormsg ( see above) when I make exactly the same request in fiddler ( works) as in the angularclient (does not work)??

user603007
  • 11,416
  • 39
  • 104
  • 168

6 Answers6

39

I know this is an old question, but I ran into the same problem and figured I could help others who are trying to figure out.

I solved it by removing 2 of the handler configurations in the Web.config:

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

I don't know exactly why it fixed the problem, but my working theory is that <remove name="OPTIONSVerbHandler" /> makes OPTIONS requests forbidden by default. When sending the request from a browser, it sends an OPTIONS request first before the PUT request, so it never gets to the PUT request because the first OPTIONS request was denied as an invalid http method on the api.

In fiddler, I am assuming that it just sends only the PUT request (I observed the same behavior using the Postman web app sending requests manually). So it skips the forbidden OPTIONS request and succeeds.

Nathan Hanna
  • 4,643
  • 3
  • 28
  • 32
  • Thanks a lot man, save my day!! Bounce my head around trying to get the CORS work. M$ really need to update the documentation and project template, the fix I made to work just by changing web.config. ` ` – Shawn Feb 16 '17 at 04:07
  • 4
    By removing this, I get 404 instead of 400 – Rosdi Kasim Nov 22 '17 at 09:40
  • Based on your working theory that buy removing the OPTIONSVerbHandler we remove the Handler that processes the OPTIONS verb, I simply removed that line from my web.config. After that it worked. Do you have any theory as to why you needed to remove as well? – Morvael Nov 23 '17 at 10:04
  • Thanks a lot man! However I've a small hint here: for my case if I was removing/commenting both `OPTIONSVerbHandler` and `ExtensionlessUrlHandler-Integrated-4.0` then I was getting `404` for existing requests. However that solved when I commented only `OPTIONSVerbHandler` – Arsen Khachaturyan May 16 '18 at 21:11
  • 1
    commenting only ` ` did it for ne – Aji Sep 13 '18 at 21:56
  • 1
    @NathanHanna You are the goat, I've been struggling with this for weeks and this fixed it. Thank you. – Gonzalo Sep 22 '20 at 15:55
11

I also faced the same issue, after few research, I did following changes to web.config and Global.asax.cs files

<configuration>

  <system.webServer>
    <directoryBrowse enabled="true" />
    <validation validateIntegratedModeConfiguration="false" />
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, PATCH, DELETE, OPTIONS" />
        <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />

      </customHeaders>
    </httpProtocol>
    <handlers>
      <remove name="WebDAV" />
      <remove name="OPTIONSVerbHandler"/>
      <remove name="TRACEVerbHandler" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />

      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="C:\windows\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="C:\windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="WebDAVModule" />
      <remove name="ApplicationInsightsWebTracking" />
      <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
    </modules>
  </system.webServer>

</configuration>

Add the following code global.ascx.cs

protected void Application_BeginRequest()
{
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
        HttpContext.Current.Response.End();
    }
}
Gehan Fernando
  • 1,221
  • 13
  • 24
1

This is almost certainly a CORS problem. I'd first read up about it to make sure you understand what this is and why it is important. And I would guess that your server configuration is not correct.

Unfortunately, I don't know much about .net, but this CORS tutorial for .net describes what you should do quite clearly.

It looks like you are missing an EnableCors annotation. It looks like you need to add something like [EnableCors("*", "*", "*")] to your controller. Explicit handling of OPTIONS is not necessary. Of course, in producition, you don't want to use wildcards for your CORS handling. They should be more specific, but this is fine for testing.

Andrew Eisenberg
  • 28,387
  • 9
  • 92
  • 148
1

Andrew is correct, it is most probably a CORS issue. What you need is to add EnableCors attribute to your Controller class so that is looks something like this

 [EnableCors(origins: "https://example.com,http://example.org", headers: "*", methods: "*")]
    public class  TestController : ApiController
    {
         // your code 

The other way to do this is mentioned in this stack overflow post

Community
  • 1
  • 1
Shashank Shekhar
  • 3,958
  • 2
  • 40
  • 52
1

Yep, an oldie but a goodie. I had the same problem and symptom but my resolution was self-inflicted. I'll share it anyway. The way our service configured the controller used in all of the MapHttpRoute calls at startup included the [DisableCors] attribute. As soon as I reconfigured my local machine startup to use a different controller, everything worked. So if you've done everything else listed here then check your controller and make sure you didn't do something stupid, like I did.

No Refunds No Returns
  • 8,092
  • 4
  • 32
  • 43
0

Apparently, in certain cases, an OPTIONS request is sent before the "real" request to determine whether the real request is safe to send due to CORS. See the following MS article at: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api and search for "Preflight Requests".

Some of the following Q&As might also help:

Community
  • 1
  • 1
djikay
  • 10,450
  • 8
  • 41
  • 52