7

My ASP.NET WebAPI & MVC application is returning a 404 error when I request a PUT or DELETE. It used to be returning a 405, but I resolved that by enabling CORS. I have tried all sorts of different solutions (disabling WebDAV, change routes, put querystring in request), but none seem to have worked for me. I'm hoping I just missed something extremely simple. Here's relevant simplified code from each relevant file in my application:

jQuery AJAX request:

$.ajax({
    url: "api/Signout?id=3",
    type: "DELETE",
    crossDomain: true,
});

SignoutController (GET and POST methods work from here just fine):

public void Delete([FromUri] int id)
{
    //Do things
}

WebApiConfig routes:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

//For another part of the application
config.Routes.MapHttpRoute(
    name: "SaveSignout",
    routeTemplate: "api/{controller}/{signout}"
);

Web.config:

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <clear />
      <add name="Access-Control-Allow-Origin" value="*" />
      <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
      <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
    </customHeaders>
  </httpProtocol>
  <modules>
    <remove name="FormsAuthenticationModule" />
    <remove name="WebDAVModule"/>
  </modules>
  <handlers>
    <remove name="WebDAV" />
    <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
    <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit"
       path="*."
       verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
       modules="IsapiModule"
       scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll"
       preCondition="classicMode,runtimeVersionv4.0,bitness64"
       responseBufferLimit="0" />
  </handlers>
</system.webServer>

RouteConfig.cs (saw this somewhere else on SO)

routes.IgnoreRoute("{*x}", new { x = @".*\.asmx(/.*)?" }); 

Fiddler DELETE request (simplified referer):

DELETE /api/Signout?id=45 HTTP/1.1
Host: localhost:51301
Connection: keep-alive
Cache-Control: no-cache
Authorization: Negotiate (large base64 here)
Pragma: no-cache
Accept: */*
Origin: http://localhost:51301
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36
Referer: http://localhost:51301/Home/Controller/Id
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8

Fiddler response:

HTTP/1.1 404 Not Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B? (base64 of full local path to api/Signout)
Persistent-Auth: true
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
WWW-Authenticate: Negotiate oRswGaADCgEAoxIEEAEAAABDh+CIwTbjqQAAAAA=
Date: Tue, 17 Feb 2015 18:05:18 GMT
Content-Length: 4966

It's just a slew of various "solutions" I've come across that all apparently worked for those involved. Where am I going wrong?

tereško
  • 58,060
  • 25
  • 98
  • 150
Scott
  • 5,338
  • 5
  • 45
  • 70
  • Is url: "api/Signouts/54" works? I am not sure though – Jenish Rabadiya Feb 17 '15 at 18:24
  • Unfortunately, that gives me the same result. – Scott Feb 17 '15 at 18:29
  • 1
    Can you try using the [HttpDelete] and [HttpPut] annotations on the specific functions in your controller? – Corstian Boerman Feb 17 '15 at 21:34
  • You have "Signout" controller, but in your jQuery you have "Signouts"... – Marcin Zablocki Feb 17 '15 at 21:46
  • @Marcin... You're right... I'll have to fix it when I get back to it tomorrow. Pretty sure this is going to be the solution though, thank you! – Scott Feb 17 '15 at 23:18
  • @MarcinZablocki Turns out I just added the "s" out of thin air while simplifying my code, and is sadly not the error. The original line was `url: (apiBaseUrl + (uri || "/Signout")) + "?id=" + encodeURIComponent(signout_id)`. I can confirm this is returning the right result by watching the XHR requests go through in Chrome dev tools, also in my post under the Fiddler DELETE request. – Scott Feb 18 '15 at 13:50
  • Also @CorstianBoerman, same result, thank you though. – Scott Feb 18 '15 at 14:36
  • if I use you sample with `url: "api/Signout?id=3"` I get `"DELETE http://localhost:64925/Home/api/Signout?id=3 404 (Not Found)"` – richaux Feb 18 '15 at 19:07
  • Yeah, unfortuatenly. That's what I'm trying to fix. Try using GET though, it works fine (for me). – Scott Feb 18 '15 at 19:09
  • Sorry, posted comment early! With `url: "/api/Signout?id=3"` it calls the method succesfully (note the `/` prefix). Your fiddle suggests this isn't the case but your ajax snippet is without the '/'. – richaux Feb 18 '15 at 19:12
  • Regrettably, my url is still correct. I can verify it by copying it into my browser and hitting it with a GET, which successfully returns my `Get` action in the same controller. – Scott Feb 18 '15 at 19:21
  • Your `web.config` handler section tells `ExtensionlessUrlHandler-ISAPI-4.0_64bit` to handle `DELETE` ... but are 32bit or Integrated appropriate? [[This answer refers](http://stackoverflow.com/a/16241628/451944)] – richaux Feb 18 '15 at 19:44
  • ...It worked. THAT WORKED! Of course, a duplicate of my question answers my question. I could have sworn I saw that question in my Google travels, though. Please post your comment an an answer so I can accept :) – Scott Feb 18 '15 at 19:54

2 Answers2

7

By default IIS does not serve DELETE requests: the system.webServer handlers defined in Web.Config can set how to process requests when your website's Application Pool has a "Managed pipeline mode" of Integrated (IIS7) or Classic (ISAPI 32 & 64 bit flavours). In your example, only ISAPI 64 bit is controlled. The following shows the other variations.

<system.webServer>
    <handlers>
      <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="%windir%\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="%windir%\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>
  </system.webServer>

Reference:

Community
  • 1
  • 1
richaux
  • 2,622
  • 2
  • 35
  • 40
3

Richaux is correct, IIS does not serve DELETE, PUT and other requests by default. However, the Web.config part in that answer is showing how it was done in the MVC4 template. When creating a MVC5 Web API project from the MVC5 template you'll see the relevant part that registers the handler for all verbs:

<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>
huysentruitw
  • 27,376
  • 9
  • 90
  • 133