【问题标题】:Web API Put Request generates an Http 405 Method Not Allowed errorWeb API Put Request 生成 Http 405 Method Not Allowed 错误
【发布时间】:2013-10-10 08:46:55
【问题描述】:

这是对我的 Web API 上的 PUT 方法的调用 - 方法中的第三行(我从 ASP.NET MVC 前端调用 Web API):

client.BaseAddresshttp://localhost/CallCOPAPI/

这里是contactUri

这里是contactUri.PathAndQuery

最后,这是我的 405 响应:

这是我的 Web API 项目中的 WebApi.config:

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

            config.Routes.MapHttpRoute(
                name: "DefaultApiGet",
                routeTemplate: "api/{controller}/{action}/{regionId}",
                defaults: new { action = "Get" },
                constraints: new { httpMethod = new HttpMethodConstraint("GET") });

            var json = config.Formatters.JsonFormatter;
            json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
            config.Formatters.Remove(config.Formatters.XmlFormatter);

我尝试将传递到PutAsJsonAsync 的路径剥离为string.Format("/api/department/{0}", department.Id)string.Format("http://localhost/CallCOPAPI/api/department/{0}", department.Id),但没有成功。

有人知道为什么我会收到 405 错误吗?

更新

根据要求,这是我的部门控制器代码(我将发布前端项目的部门控制器代码以及 WebAPI 的部门 ApiController 代码):

前端部门控制器

namespace CallCOP.Controllers
{
    public class DepartmentController : Controller
    {
        HttpClient client = new HttpClient();
        HttpResponseMessage response = new HttpResponseMessage();
        Uri contactUri = null;

        public DepartmentController()
        {
            // set base address of WebAPI depending on your current environment
            client.BaseAddress = new Uri(ConfigurationManager.AppSettings[string.Format("APIEnvBaseAddress-{0}", CallCOP.Helpers.ConfigHelper.COPApplEnv)]);

            // Add an Accept header for JSON format.
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
        }

        // need to only get departments that correspond to a Contact ID.
        // GET: /Department/?regionId={0}
        public ActionResult Index(int regionId)
        {
            response = client.GetAsync(string.Format("api/department/GetDeptsByRegionId/{0}", regionId)).Result;
            if (response.IsSuccessStatusCode)
            {
                var departments = response.Content.ReadAsAsync<IEnumerable<Department>>().Result;
                return View(departments);
            }
            else
            {
                LoggerHelper.GetLogger().InsertError(new Exception(string.Format(
                    "Cannot retrieve the list of department records due to HTTP Response Status Code not being successful: {0}", response.StatusCode)));
                return RedirectToAction("Index");
            }

        }

        //
        // GET: /Department/Create

        public ActionResult Create(int regionId)
        {
            return View();
        }

        //
        // POST: /Department/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(int regionId, Department department)
        {
            department.RegionId = regionId;
            response = client.PostAsJsonAsync("api/department", department).Result;
            if (response.IsSuccessStatusCode)
            {
                return RedirectToAction("Edit", "Region", new { id = regionId });
            }
            else
            {
                LoggerHelper.GetLogger().InsertError(new Exception(string.Format(
                    "Cannot create a new department due to HTTP Response Status Code not being successful: {0}", response.StatusCode)));
                return RedirectToAction("Edit", "Region", new { id = regionId });
            }
        }

        //
        // GET: /Department/Edit/5

        public ActionResult Edit(int id = 0)
        {
            response = client.GetAsync(string.Format("api/department/{0}", id)).Result;
            Department department = response.Content.ReadAsAsync<Department>().Result;
            if (department == null)
            {
                return HttpNotFound();
            }
            return View(department);
        }

        //
        // POST: /Department/Edit/5

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(int regionId, Department department)
        {
            response = client.GetAsync(string.Format("api/department/{0}", department.Id)).Result;
            contactUri = response.RequestMessage.RequestUri;
            response = client.PutAsJsonAsync(string.Format(contactUri.PathAndQuery), department).Result;
            if (response.IsSuccessStatusCode)
            {
                return RedirectToAction("Index", new { regionId = regionId });
            }
            else
            {
                LoggerHelper.GetLogger().InsertError(new Exception(string.Format(
                    "Cannot edit the department record due to HTTP Response Status Code not being successful: {0}", response.StatusCode)));
                return RedirectToAction("Index", new { regionId = regionId });
            }
        }

        //
        // GET: /Department/Delete/5

        public ActionResult Delete(int id = 0)
        {
            response = client.GetAsync(string.Format("api/department/{0}", id)).Result;
            Department department = response.Content.ReadAsAsync<Department>().Result;

            if (department == null)
            {
                return HttpNotFound();
            }
            return View(department);
        }

        //
        // POST: /Department/Delete/5

        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int regionId, int id)
        {
            response = client.GetAsync(string.Format("api/department/{0}", id)).Result;
            contactUri = response.RequestMessage.RequestUri;
            response = client.DeleteAsync(contactUri).Result;
            return RedirectToAction("Index", new { regionId = regionId });
        }
    }
}

Web API 部门 ApiController

namespace CallCOPAPI.Controllers
{
    public class DepartmentController : ApiController
    {
        private CallCOPEntities db = new CallCOPEntities(HelperClasses.DBHelper.GetConnectionString());

        // GET api/department
        public IEnumerable<Department> Get()
        {
            return db.Departments.AsEnumerable();
        }

        // GET api/department/5
        public Department Get(int id)
        {
            Department dept = db.Departments.Find(id);
            if (dept == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }

            return dept;
        }

        // this should accept a contact id and return departments related to the particular contact record
        // GET api/department/5
        public IEnumerable<Department> GetDeptsByRegionId(int regionId)
        {
            IEnumerable<Department> depts = (from i in db.Departments
                                             where i.RegionId == regionId 
                                             select i);
            return depts;
        }

        // POST api/department
        public HttpResponseMessage Post(Department department)
        {
            if (ModelState.IsValid)
            {
                db.Departments.Add(department);
                db.SaveChanges();

                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, department);
                return response;
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
        }

        // PUT api/department/5
        public HttpResponseMessage Put(int id, Department department)
        {
            if (!ModelState.IsValid)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }

            if (id != department.Id)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }

            db.Entry(department).State = EntityState.Modified;

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }

            return Request.CreateResponse(HttpStatusCode.OK);
        }

        // DELETE api/department/5
        public HttpResponseMessage Delete(int id)
        {
            Department department = db.Departments.Find(id);
            if (department == null)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

            db.Departments.Remove(department);

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }

            return Request.CreateResponse(HttpStatusCode.OK, department);
        }
    }
}

【问题讨论】:

  • 您不应该在操作方法定义之前使用[HttpPut] 吗? ([HttpPost][HttpDelete] 在适当的情况下也是如此)
  • @ChrisPratt 澄清一下,您的意思是把[HttpPut] 放在WebAPI 控制器(ApiController)上,对吧?因为部门的前端控制器(编辑方法)有一个[HttpPost] 属性。
  • @ChrisPratt ValuesController(WebAPI 模板附带的那个)在 Put/Post/Delete 方法上没有 [HttpPut] 等属性..
  • 是的,我有理由确定它需要 Web API 端的那些。就个人而言,我一直只是将 AttributeRouting 用于 Web API 的东西,所以我的回忆有点粗略。
  • 显然是 WebDAV 的问题。我检查了我的本地 IIS(Windows 功能)以确保它没有安装,它说它不是......无论如何我发布了一个答案,基本上删除了我的 web.config 中的模块 WebDAV。

标签: c# asp.net-mvc json asp.net-web-api http-status-code-405


【解决方案1】:

这个简单的问题会让人头疼!

我可以看到您的控制器 EDIT (PUT) 方法需要 2 个参数:a)一个 int id,和 b)一个部门对象。

这是从 VS 生成此代码时的默认代码 > 添加带有读/写选项的控制器。但是,你必须记住使用这两个参数来消费这个服务,否则你会得到错误 405。

在我的例子中,我不需要PUT 的 id 参数,所以我只是将它从标题中删除了......在几个小时没有注意到它之后!如果将其保留在那里,则名称也必须保留为 id,除非您继续对配置进行必要的更改。

【讨论】:

    【解决方案2】:

    您可以从 GUI 中手动删除特定于 IIS 的 webdav 模块。
    1) 转到 II。
    2) 转到相应的站点。
    3) 打开“处理程序映射”
    4) 向下滚动并选择 WebDav 模块。右键单击它并删除它。

    注意:这也会更新您的 web 应用程序的 web.config。

    【讨论】:

      【解决方案3】:

      在我的情况下,由于路由(“api/images”)与同名文件夹(“~/images”)冲突,静态处理程序调用了错误 405。

      【讨论】:

        【解决方案4】:

        我在 IIS 8.5 上运行 ASP.NET MVC 5 应用程序。我尝试了此处发布的所有变体,这就是我的web.config 的样子:

        <system.webServer>
            <modules runAllManagedModulesForAllRequests="true">
                <remove name="WebDAVModule"/> <!-- add this -->
            </modules>  
            <handlers>      
              <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
              <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
              <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
              <remove name="WebDAV" />
              <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>
        

        我无法从我的服务器上卸载 WebDav,因为我没有管理员权限。此外,有时我会在 .css 和 .js 文件上获得method not allowed。最后,通过上面的配置设置一切又开始工作了。

        【讨论】:

          【解决方案5】:

          造成这种情况的另一个原因可能是您没有为“id”使用默认变量名,实际上是:id。

          【讨论】:

            【解决方案6】:

            用 [FromBody] 装饰其中一个动作参数为我解决了这个问题:

            public async Task<IHttpActionResult> SetAmountOnEntry(string id, [FromBody]int amount)
            

            但是,如果在方法参数中使用复杂对象,ASP.NET 会正确推断:

            public async Task<IHttpActionResult> UpdateEntry(string id, MyEntry entry)
            

            【讨论】:

              【解决方案7】:

              所以,我检查了 Windows 功能以确保我没有安装这个名为 WebDAV 的东西,它说我没有。无论如何,我继续将以下内容放在我的 web.config 中(前端和 WebAPI,只是为了确定),它现在可以工作了。我把这个放在&lt;system.webServer&gt;里面。

              <modules runAllManagedModulesForAllRequests="true">
                  <remove name="WebDAVModule"/> <!-- add this -->
              </modules>
              

              此外,通常需要在处理程序中将以下内容添加到web.config。感谢巴巴克

              <handlers>
                  <remove name="WebDAV" />
                  ...
              </handlers>
              

              【讨论】:

              • 哈哈......是的......我正要放弃。嗯是的。 WebDAV 必须已在您的applicationhost.config 中启用。很高兴你已经修好了。
              • 你可能还需要添加这个:&lt;handlers&gt;&lt;remove name="WebDAV" /&gt;...
              • 将这个 only 添加到我的 WebApi web.config 并且它工作正常。
              • 即使在 IE10 中即使没有此配置也可以正常工作,但我只需要在 WebApi web.config 中进行操作即可使其在 Chrome 浏览器中工作。
              • 感谢您回答这个非常烦人的问题。为什么会发生这种情况?
              【解决方案8】:

              您的客户端应用程序和服务器应用程序必须在同一个域下,例如:

              客户端 - 本地主机

              服务器 - 本地主机

              而不是:

              客户端 - 本地主机:21234

              服务器 - 本地主机

              【讨论】:

              • 我不这么认为。创建服务的目的是从另一个域调用
              • 你正在考虑一个跨域请求,它会给你一个来自服务器的 200 响应,但浏览器将执行其“无跨域请求”规则并且不接受响应。问题是指 405“方法不允许”响应,一个不同的问题。
              • CORS 会给出 405 "Method Not Allowed",例如:Request URL:testapi.nottherealsite.com/api/Reporting/RunReport Request Method:OPTIONS Status Code:405 Method Not Allowed 请看这里stackoverflow.com/questions/12458444/…
              • 你指的是CORS问题。
              【解决方案9】:

              WebDav-SchmebDav.. ..确保使用正确的 ID 创建 url。不要发http://www.fluff.com/api/Fluff?id=MyID,发http://www.fluff.com/api/Fluff/MyID

              例如。

              PUT http://www.fluff.com/api/Fluff/123 HTTP/1.1
              Host: www.fluff.com
              Content-Length: 11
              
              {"Data":"1"}
              

              这让我的蛋蛋打了一小会儿,完全尴尬。

              【讨论】:

              • 对我来说还有一个问题:PUT 动作不能将数据绑定到原始类型参数。我不得不将public int PutFluffColor(int Id, int colorCode) 更改为public int PutFluffColor(int Id, UpdateFluffColorModel model)
              • 希望我能为 WebDav-SchmebDav 投票两次
              • 经过 8 多个小时的工作达到解决方案后,每个人都在推荐 web.config 更改它太神奇了,没有人甚至没有谈论过这种可能性。
              【解决方案10】:

              将此添加到您的web.config。你需要告诉 IIS PUT PATCH DELETEOPTIONS 是什么意思。以及调用哪个IHttpHandler

              <configuation>
                  <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>
              </configuration>
              

              还要检查您是否启用了 WebDAV。

              【讨论】:

              • 我已经有了。我假设这是放在 Web API 项目中,而不是我的前端 MVC 项目中,对吧?
              • 我没有安装 WebDAV。另外,您是说上面的 web.config 代码需要放在调用 Web API 的项目的 web.config 中吗?
              • 它实际上在两个 web.configs... :(
              • 哦不...我以为你是从 MVC 项目中引用 Web API 项目。
              • 你能贴出DepartmentController的代码清单吗?所有的。问题出在你的Web API项目,不知道怎么处理PUT,就是405的意思。检查 GET 是否有效,只是为了排除路由。 PS。尝试复制粘贴代码而不是屏幕截图。 PPS,不要使用Task.Result,在某些情况下你会遇到不相关的线程问题。只需将整个方法转换为异步等待即可。更不用说它会创建同步的多线程阻塞代码(比单线程慢)。
              猜你喜欢
              • 2018-06-29
              • 2015-07-29
              • 2015-01-20
              • 1970-01-01
              • 1970-01-01
              • 2015-07-26
              • 2021-01-21
              • 1970-01-01
              • 2014-08-31
              相关资源
              最近更新 更多