排查在 Visual Studio 中工作并在生产 IIS 服务器上失败的 Web API2 应用的问题

本文档介绍如何对部署到生产 IIS 服务器的 Web API2 应用进行故障排除。 它解决了常见的 HTTP 405 和 501 错误。

本教程中使用的软件

Web API 应用通常使用多个 HTTP 谓词:GET、POST、PUT、DELETE,有时还有 PATCH。 话虽然这样说,开发人员可能会遇到这些谓词由另一个 IIS 模块在其生产 IIS 服务器上实现的情况,这会导致在 Visual Studio 或开发服务器上正常工作的 Web API 控制器在部署到生产 IIS 服务器时返回 HTTP 405 错误。

导致 HTTP 405 错误的原因

了解如何排查 HTTP 405 错误的第一步是了解 HTTP 405 错误的实际含义。 HTTP 的主要控制文档是 RFC 2616,它将 HTTP 405 状态代码定义为 “不允许的方法”,并进一步将此状态代码描述为“不允许请求 URI 标识的资源使用 Request-Line 中指定的方法” 的情况。换句话说,HTTP 客户端请求的特定 URL 不允许使用 HTTP 谓词。

作为简要回顾,下面是 RFC 2616、RFC 4918 和 RFC 5789 中定义的几种最常用的 HTTP 方法:

HTTP 方法 说明
GET 此方法用于从 URI 检索数据,它可能是最常用的 HTTP 方法。
HEAD 此方法非常类似于 GET 方法,只不过它实际上不会从请求 URI 检索数据 - 它只是检索 HTTP 状态。
POST 此方法通常用于将新数据发送到 URI;POST 通常用于提交表单数据。
PUT 此方法通常用于将原始数据发送到 URI;PUT 通常用于将 JSON 或 XML 数据提交到 Web API 应用程序。
DELETE 此方法用于从 URI 中删除数据。
OPTIONS 此方法通常用于检索 URI 支持的 HTTP 方法列表。
COPY MOVE 这两种方法与 WebDAV 一起使用,其用途一目了然。
MKCOL 此方法与 WebDAV 一起使用,用于创建集合 (例如指定 URI 处) 目录。
PROPFIND PROPPATCH 这两种方法与 WebDAV 一起使用,它们用于查询或设置 URI 的属性。
LOCK UNLOCK 这两种方法与 WebDAV 一起使用,它们用于在创作时锁定/解锁由请求 URI 标识的资源。
PATCH 此方法用于修改现有 HTTP 资源。

将其中一种 HTTP 方法配置为在服务器上使用时,服务器将使用 HTTP 状态和适合请求的其他数据进行响应。 (例如,GET 方法可能会收到 HTTP 200 OK 响应,PUT 方法可能会收到 HTTP 201 Created 响应。)

如果未将 HTTP 方法配置为在服务器上使用,则服务器将响应 HTTP 501 未实现 错误。

但是,当 HTTP 方法配置为在服务器上使用,但已针对给定 URI 禁用该方法时,服务器将响应 HTTP 405 方法不允许 错误。

示例 HTTP 405 错误

以下示例 HTTP 请求和响应说明了以下情况:HTTP 客户端尝试将 PUT 值传递给 Web 服务器上的 Web API 应用,并且服务器返回 HTTP 错误,指出不允许使用 PUT 方法:

HTTP 请求:

PUT /api/values/1 HTTP/1.1
Content-type: application/json
Host: localhost
Accept: */*
Content-Length: 12

"Some Value"

HTTP 响应:

HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Date: Wed, 15 May 2013 02:38:57 GMT
Content-Length: 72

{"Message":"The requested resource does not support http method 'PUT'."}

在此示例中,HTTP 客户端向 Web 服务器上的 Web API 应用程序的 URL 发送了有效的 JSON 请求,但服务器返回了一条 HTTP 405 错误消息,指示不允许对 URL 使用 PUT 方法。 相比之下,如果请求 URI 与 Web API 应用程序的路由不匹配,则服务器将返回 HTTP 404 未找到 错误。

解决 HTTP 405 错误

不允许使用特定 HTTP 谓词的原因有多种,但 IIS 中此错误的主要原因有一种主要方案:为同一谓词/方法定义了多个处理程序,其中一个处理程序阻止预期的处理程序处理请求。 为了说明,IIS 根据 applicationHost.configweb.config 文件中的顺序处理程序条目处理从第一个到最后一个的处理程序,其中路径、谓词、资源等的第一个匹配组合将用于处理请求。

以下示例是 IIS 服务器的 applicationHost.config 文件的摘录,该文件在使用 PUT 方法将数据提交到 Web API 应用程序时返回了 HTTP 405 错误。 在此摘录中,定义了多个 HTTP 处理程序,并且每个处理程序都有一组不同的 HTTP 方法,为其配置了它 - 列表中的最后一个条目是静态内容处理程序,它是在其他处理程序有机会检查请求之后使用的默认处理程序:

<handlers accessPolicy="Read, Script">
   <add name="WebDAV"
      path="*"
      verb="PROPFIND,PROPPATCH,MKCOL,PUT,COPY,DELETE,MOVE,LOCK,UNLOCK"
      modules="WebDAVModule"
      resourceType="Unspecified"
      requireAccess="None" />
   <add name="ISAPI-dll"
      path="*.dll"
      verb="*"
      modules="IsapiModule"
      resourceType="File"
      requireAccess="Execute"
      allowPathInfo="true" />
   <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit"
      path="*."
      verb="GET,HEAD,POST,DEBUG"
      modules="IsapiModule"
      scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll"
      preCondition="classicMode,runtimeVersionv4.0,bitness64"
      responseBufferLimit="0" />

   <!-- Additional handlers will be defined here. -->

   <add name="StaticFile"
      path="*"
      verb="*"
      modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule"
      resourceType="Either"
      requireAccess="Read" />
</handlers>

在前面的示例中,用于 Web API) 的 ASP.NET (的 WebDAV 处理程序和无扩展 URL 处理程序已明确定义为单独的 HTTP 方法列表。 请注意,ISAPI DLL 处理程序已为所有 HTTP 方法配置,尽管此配置不一定会导致错误。 但是,在排查 HTTP 405 错误时,需要考虑此类配置设置。

在前面的示例中,ISAPI DLL 处理程序不是问题;事实上,问题未在 IIS 服务器的 applicationHost.config 文件中定义 - 此问题是由在 Visual Studio 中创建 Web API 应用程序时 在web.config 文件中创建的条目引起的。 应用程序的 web.config 文件中的以下摘录显示了问题的位置:

<handlers accessPolicy="Read, Script">
   <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>

在此摘录中,将重新定义 ASP.NET 的无扩展 URL 处理程序,以包含将用于 Web API 应用程序的其他 HTTP 方法。 但是,由于为 WebDAV 处理程序定义了一组类似的 HTTP 方法,因此会发生冲突。 在此特定情况下,WebDAV 处理程序由 IIS 定义和加载,即使对包含 Web API 应用程序的网站禁用 WebDAV 也是如此。 在处理 HTTP PUT 请求期间,IIS 会调用 WebDAV 模块,因为它是为 PUT 谓词定义的。 调用 WebDAV 模块时,它会检查其配置,并发现它已禁用,因此对于任何类似于 WebDAV 请求的请求,它将返回 HTTP 405 方法不允许 错误。 若要解决此问题,应从定义 Web API 应用程序的网站的 HTTP 模块列表中删除 WebDAV。 以下示例显示可能如下所示:

<handlers accessPolicy="Read, Script">
   <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>

在将应用程序从开发环境发布到 IIS 生产环境之后,通常会遇到这种情况,这是因为开发和生产环境之间的处理程序/模块列表不同。 例如,如果使用 Visual Studio 2012 或更高版本开发 Web API 应用程序,则IIS Express是用于测试的默认 Web 服务器。 此开发 Web 服务器是服务器产品中附带的完整 IIS 功能的缩减版本,此开发 Web 服务器包含为开发方案添加的一些更改。 例如,WebDAV 模块通常安装在运行完整版 IIS 的生产 Web 服务器上,尽管它可能未使用。 IIS 的开发版本 (IIS Express) 安装 WebDAV 模块,但 WebDAV 模块的条目被有意注释掉,因此,除非专门更改IIS Express配置设置以将 WebDAV 功能添加到IIS Express安装,否则永远不会在IIS Express上加载 WebDAV 模块。 因此,Web 应用程序可能在开发计算机上正常工作,但在将 Web API 应用程序发布到生产 IIS Web 服务器时,可能会遇到 HTTP 405 错误。

HTTP 501 错误

  • 指示尚未在服务器上实现特定功能。
  • 通常意味着 IIS 设置中没有定义与 HTTP 请求匹配的处理程序:
    • 可能表示某些内容未在 IIS 上正确安装,或者
    • 某些内容修改了 IIS 设置,因此没有定义支持特定 HTTP 方法的处理程序。

若要解决此问题,需要重新安装尝试使用 HTTP 方法的任何应用程序,而该应用程序没有相应的模块或处理程序定义。

总结

当 Web 服务器对请求的 URL 不允许使用 HTTP 方法时,会导致 HTTP 405 错误。 当为特定谓词定义了特定处理程序,并且该处理程序正在重写预期要处理请求的处理程序时,通常会看到此情况。