Die Web-API-Put-Anforderung generiert einen Fehler "HTTP 405-Methode nicht zulässig"

134

Hier ist der Aufruf der PUTMethode in meiner Web-API - die dritte Zeile in der Methode (ich rufe die Web-API von einem ASP.NET MVC-Frontend aus auf):

Geben Sie hier die Bildbeschreibung ein

client.BaseAddressist http://localhost/CallCOPAPI/.

Hier ist contactUri:

Geben Sie hier die Bildbeschreibung ein

Hier ist contactUri.PathAndQuery:

Geben Sie hier die Bildbeschreibung ein

Und zum Schluss hier meine 405-Antwort:

Geben Sie hier die Bildbeschreibung ein

Hier ist die WebApi.config in meinem Web-API-Projekt:

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

Ich habe versucht, den Weg, der PutAsJsonAsynczu string.Format("/api/department/{0}", department.Id)und string.Format("http://localhost/CallCOPAPI/api/department/{0}", department.Id)ohne Glück führt, abzustreifen.

Hat jemand eine Idee, warum ich den 405-Fehler erhalte?

AKTUALISIEREN

Auf Anfrage ist hier mein Abteilungscontroller-Code (ich werde sowohl den Abteilungscontroller-Code für mein Front-End-Projekt als auch den Abteilungs-ApiController-Code für die WebAPI veröffentlichen):

Controller der Front-End-Abteilung

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-Abteilung 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);
        }
    }
}
Mike Marks
quelle
Sollten Sie nicht [HttpPut]vor der Definition der Aktionsmethode verwenden? ( [HttpPost]und [HttpDelete]gegebenenfalls auch)
Chris Pratt
@ChrisPratt Um ganz klar zu sein, meinen Sie, [HttpPut]den WebAPI-Controller (ApiController) einzuschalten, oder? Weil der Front-End-Controller für Abteilung (Bearbeitungsmethode) ein [HttpPost]Attribut hat.
Mike Marks
1
@ChrisPratt Der ValuesController (derjenige, der mit der WebAPI-Vorlage geliefert wird) hat keine [HttpPut]usw. Attribute für die Put / Post / Delete-Methoden.
Mike Marks
Ja, ich bin mir ziemlich sicher, dass diese auf der Web-API-Seite benötigt werden. Persönlich habe ich AttributeRouting immer nur für Web-API-Inhalte verwendet, daher ist meine Erinnerung etwas lückenhaft.
Chris Pratt
Anscheinend war es das WebDAV-Ding. Ich habe mein lokales IIS (Windows-Funktionen) überprüft, um sicherzustellen, dass es nicht installiert war, und es wurde gesagt, dass es nicht ... sowieso habe ich eine Antwort darauf gepostet und im Grunde das Modul WebDAV in meinem Web entfernt .config.
Mike Marks

Antworten:

304

Also habe ich die Windows-Funktionen überprüft, um sicherzustellen, dass ich dieses Ding namens WebDAV nicht installiert habe, und es heißt, dass ich es nicht getan habe. Wie auch immer, ich habe Folgendes in meine web.config eingefügt (sowohl Front-End als auch WebAPI, nur um sicherzugehen), und es funktioniert jetzt. Ich habe das hineingelegt <system.webServer>.

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

Darüber hinaus ist es häufig erforderlich, web.configin den Handlern Folgendes hinzuzufügen . Danke an Babak

<handlers>
    <remove name="WebDAV" />
    ...
</handlers>
Mike Marks
quelle
2
Haha ... ja ... ich wollte gerade aufgeben. Also ja. WebDAV muss in Ihrem aktiviert sein applicationhost.config. Ich bin froh, dass du es behoben hast.
Aron
9
Möglicherweise müssen Sie auch Folgendes hinzufügen:<handlers><remove name="WebDAV" />...
Babak
14
Dies wurde nur zu meiner WebApi web.config hinzugefügt und es hat funktioniert.
Fordy
Obwohl es in IE10 auch ohne diese Konfiguration einwandfrei funktionierte, musste ich dies nur in WebApi web.config tun, damit es im Chrome-Browser funktioniert.
Dennis R
1
Vielen Dank für die Antwort auf dieses wirklich nervige Problem. Warum tritt das überhaupt auf?
Scott Wilson
23

WebDav-SchmebDav .. ..Stellen Sie sicher, dass Sie die URL mit der ID korrekt erstellen. Senden Sie es nicht wie http://www.fluff.com/api/Fluff?id=MyID , sondern wie http://www.fluff.com/api/Fluff/MyID .

Z.B.

PUT http://www.fluff.com/api/Fluff/123 HTTP/1.1
Host: www.fluff.com
Content-Length: 11

{"Data":"1"}

Dies sprengte meine Eier für eine kleine Ewigkeit, völlige Verlegenheit.

Molibar
quelle
3
Ein zusätzlicher Ball Buster für mich: PUT-Aktionen können keine Daten an primitive Typparameter binden. Ich musste ändern public int PutFluffColor(int Id, int colorCode)zupublic int PutFluffColor(int Id, UpdateFluffColorModel model)
Josh Noe
4
Ich wünschte, ich könnte dies zweimal für das WebDav-SchmebDav
Noel
1
Nach mehr als 8 Stunden Arbeit, die zur Lösung führen, empfiehlt jeder, die web.config zu ändern. Es ist so erstaunlich, dass niemand über diese Möglichkeit gesprochen hat.
Sairfan
22

Fügen Sie dies Ihrem hinzu web.config. Sie müssen IIS mitteilen, was PUT PATCH DELETEund was OPTIONSbedeutet. Und welche IHttpHandlerman aufruft.

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

Überprüfen Sie auch, ob WebDAV nicht aktiviert ist.

Aron
quelle
Das habe ich schon. Ich gehe davon aus, dass dies in das Web-API-Projekt aufgenommen werden soll, nicht in mein Front-End-MVC-Projekt, oder?
Mike Marks
Ich habe WebDAV nicht installiert. Wollen Sie außerdem sagen, dass der obige web.config-Code in der web.config des Projekts platziert werden muss, das die Web-API aufruft?
Mike Marks
Es ist eigentlich in beiden web.configs ... :(
Mike Marks
Oh nein ... Ich dachte, Sie verweisen auf ein Web-API-Projekt aus einem MVC-Projekt.
Aron
1
Können Sie die Codeliste des DepartmentController veröffentlichen? Alles davon. Das Problem liegt in Ihrem Web-API-Projekt, und es weiß nicht, wie es damit umgehen PUTsoll. Das bedeutet 405. Überprüfen Sie, ob GET funktioniert, um das Routing auszuschließen. PS. Versuchen Sie, den Einfügecode anstelle des Screenshots zu kopieren. PPS, NICHT VERWENDEN Task.Result, in bestimmten Situationen treten nicht verwandte Threading-Probleme auf. Verwandeln Sie stattdessen einfach die gesamte Methode in asynchrones Warten. Ganz zu schweigen davon, dass synchroner, blockierter Multithread-Code erstellt wird (langsamer als Single-Threaded).
Aron
14

Ich führe eine ASP.NET MVC 5-Anwendung unter IIS 8.5 aus. Ich habe alle hier veröffentlichten Variationen ausprobiert und so sehe ich web.configaus:

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

Ich konnte WebDav nicht von meinem Server deinstallieren, da ich keine Administratorrechte hatte. Außerdem bekam ich manchmal die method not allowedDateien on .css und .js. Am Ende funktionierte mit der oben eingerichteten Konfiguration alles wieder.

jpgrassi
quelle
5

Das Dekorieren eines der Aktionsparameter mit [FromBody] löste das Problem für mich:

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

ASP.NET würde jedoch korrekt darauf schließen, wenn ein komplexes Objekt im Methodenparameter verwendet würde:

public async Task<IHttpActionResult> UpdateEntry(string id, MyEntry entry)
Владимiръ
quelle
1

Eine andere Ursache hierfür könnte sein, dass Sie nicht den Standardvariablennamen für die "ID" verwenden, die tatsächlich lautet: id.

Adam Levitt
quelle
0

In meinem Fall wurde der Fehler 405 vom statischen Handler aufgerufen, weil die Route ("api / images") mit dem gleichnamigen Ordner ("~ / images") in Konflikt stand.

Petr Šugar
quelle
0

Sie können das webdav-Modul manuell aus der GUI für das jeweilige IIS entfernen.
1) Gehe zu den IIs.
2) Gehen Sie zur jeweiligen Site.
3) Öffnen Sie "Handler-Zuordnungen"
4) Scrollen Sie nach unten und wählen Sie das WebDav-Modul aus. Klicken Sie mit der rechten Maustaste darauf und löschen Sie es.

Hinweis: Dadurch wird auch Ihre web.config der Web-App aktualisiert.

Naveen Kumar GC
quelle
-1

Ihre Clientanwendung und Serveranwendung müssen sich unter derselben Domäne befinden, zum Beispiel:

client - localhost

Server - localhost

und nicht :

client - localhost: 21234

Server - localhost

Lev K.
quelle
2
Das glaube ich nicht. Das Ziel der Erstellung eines Dienstes ist es, von einer anderen Domain aus anzurufen
Ozan BAYRAM
Sie denken an eine domänenübergreifende Anfrage, die Ihnen eine Antwort von 200 vom Server gibt, aber der Browser erzwingt die Regel "Keine domänenübergreifenden Anfragen" und akzeptiert die Antwort nicht. Die Frage bezieht sich auf eine 405-Antwort "Methode nicht zulässig", ein anderes Problem.
Josh Noe
CORS gibt 405 "Methode nicht zulässig" aus, zum Beispiel: Anforderungs-URL: testapi.nottherealsite.com/api/Reporting/RunReport Anforderungsmethode: OPTIONEN Statuscode: 405 Methode nicht zulässig Bitte lesen Sie hier stackoverflow.com/questions/12458444/…
Lev K.
Sie beziehen sich auf das CORS-Problem.
user3151766