La richiesta di inserimento dell'API Web genera un errore Metodo 405 non consentito


134

Ecco la chiamata al PUTmetodo sulla mia API Web - la terza riga del metodo (sto chiamando l'API Web da un front-end MVC ASP.NET):

inserisci qui la descrizione dell'immagine

client.BaseAddresslo è http://localhost/CallCOPAPI/.

Ecco contactUri:

inserisci qui la descrizione dell'immagine

Ecco contactUri.PathAndQuery:

inserisci qui la descrizione dell'immagine

E infine, ecco la mia risposta 405:

inserisci qui la descrizione dell'immagine

Ecco il WebApi.config nel mio progetto API Web:

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

Ho provato a smontare il percorso che viene passato in PutAsJsonAsynca string.Format("/api/department/{0}", department.Id)e string.Format("http://localhost/CallCOPAPI/api/department/{0}", department.Id)senza fortuna.

Qualcuno ha qualche idea sul perché sto ricevendo l'errore 405?

AGGIORNARE

Come da richiesta, ecco il mio codice controller di dipartimento (inserirò sia il codice controller dipartimento per il mio progetto front-end, sia il codice dipartimento ApiController per il WebAPI):

Controller di reparto front-end

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

ApiController Dipartimento API Web

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

Non dovresti usare [HttpPut]prima della definizione del metodo di azione? ( [HttpPost]e [HttpDelete]se del caso anche)
Chris Pratt,

@ChrisPratt Giusto per essere chiari, intendi mettere [HttpPut]il controller WebAPI (ApiController), giusto? Perché il controller front-end per il reparto (metodo Modifica) ha un [HttpPost]attributo.
Mike Marks

1
@ChrisPratt Il ValuesController (quello fornito con il modello WebAPI) non ha [HttpPut], ecc. Attributi sui metodi Put / Post / Delete ..
Mike Marks

Sì, sono ragionevolmente sicuro che abbia bisogno di quelli sul lato API Web. Personalmente, ho sempre usato AttributeRouting per cose API Web, quindi il mio ricordo è un po 'impreciso.
Chris Pratt,

Apparentemente è stata la cosa di WebDAV .. Ho controllato il mio IIS locale (Funzionalità Windows) per assicurarmi che non fosse installato e ha detto che non lo era ... comunque ho pubblicato una risposta a ciò, sostanzialmente rimuovendo il modulo WebDAV nel mio web .config.
Mike Marks

Risposte:


304

Quindi, ho controllato le funzionalità di Windows per assicurarmi di non aver installato questa cosa chiamata WebDAV, e ha detto che non l'ho fatto. Ad ogni modo, sono andato avanti e ho inserito quanto segue nel mio web.config (sia front-end che WebAPI, per essere sicuri), e ora funziona. L'ho inserito dentro <system.webServer>.

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

Inoltre, è spesso necessario aggiungere quanto segue web.confignei gestori. Grazie a Babak

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

2
Haha ... sì ... stavo per arrendermi. Quindi si WebDAV deve essere abilitato nel tuo applicationhost.config. Sono contento che tu l'abbia corretto.
Aron,

9
Potrebbe anche essere necessario aggiungere anche questo:<handlers><remove name="WebDAV" />...
Babak

14
Aggiunto questo solo al mio web.config WebApi e ha funzionato.
Fordy,

Anche se in IE10 ha funzionato bene anche senza questa configurazione, ho dovuto fare solo in WebApi web.config per farlo funzionare nel browser Chrome.
Dennis R,

1
Grazie per la risposta a questo problema davvero fastidioso. Perché questo si verifica in primo luogo?
Scott Wilson,

23

WebDav-SchmebDav .. .. assicurati di creare correttamente l'URL con l'ID. Non inviarlo come http://www.fluff.com/api/Fluff?id=MyID , inviarlo come http://www.fluff.com/api/Fluff/MyID .

Per esempio.

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

{"Data":"1"}

Questo mi ha spezzato le palle per una piccola eternità, imbarazzo totale.


3
Un ulteriore problema per me: le azioni PUT non possono associare i dati a parametri di tipo primitivo. Ho dovuto passare public int PutFluffColor(int Id, int colorCode)apublic int PutFluffColor(int Id, UpdateFluffColorModel model)
Josh Noe,

4
Vorrei poter votare due volte per il WebDav-SchmebDav
Noel il

1
dopo più di 8 ore di lavoro per raggiungere la soluzione, tutti raccomandano web.config che cambia così incredibilmente che nessuno ha parlato di questa possibilità.
Sairfan,

22

Aggiungi questo al tuo web.config. Devi dire a IIS cosa PUT PATCH DELETEe cosa OPTIONSsignifica. E quale IHttpHandlerinvocare.

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

Verifica inoltre che WebDAV non sia abilitato.


L'ho già. Suppongo che questo debba essere inserito nel progetto API Web, non nel mio progetto MVC front-end, giusto?
Mike Marks,

Non ho installato WebDAV. Inoltre, stai dicendo che il codice web.config sopra deve essere inserito nel web.config del progetto che effettua la chiamata all'API Web?
Mike Marks,

In realtà è in entrambi i web.configs ... :(
Mike Marks

Oh no ... Pensavo stavi facendo riferimento a un progetto API Web da un progetto MVC.
Aron,

1
Puoi pubblicare l'elenco dei codici di DepartmentController? Tutto. Il problema sta nel tuo progetto API Web e non sa come gestirlo PUT, questo è ciò che significa 405. Verifica che GET funzioni, solo per escludere il routing. PS. Prova a copiare il codice incolla anziché lo screenshot. PPS, NON UTILIZZARE Task.Result, otterrai problemi di threading non correlati in determinate situazioni. Basta invece trasformare l'intero metodo in asincrono. Per non parlare del fatto che crea codice bloccato sincrono, multithread (più lento del singolo thread).
Aron,

14

Sto eseguendo un'applicazione ASP.NET MVC 5 su IIS 8.5. Ho provato tutte le varianti pubblicate qui, e questo è come web.configappare il mio :

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

Non sono riuscito a disinstallare WebDav dal mio server perché non avevo i privilegi di amministratore. Inoltre, a volte method not allowedottenevo i file .css e .js. Alla fine, con la configurazione sopra impostata, tutto ha ripreso a funzionare.


5

Decorare uno dei parametri di azione con [FromBody] ha risolto il problema per me:

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

Tuttavia ASP.NET lo dedurrebbe correttamente se nel parametro del metodo fosse utilizzato un oggetto complesso:

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

1

Un'altra causa potrebbe essere se non si utilizza il nome della variabile predefinita per "id" che è in realtà: id.


0

Nel mio caso l'errore 405 è stato invocato dal gestore statico a causa di route ("api / images") in conflitto con la cartella con lo stesso nome ("~ / images").


0

È possibile rimuovere manualmente il modulo webdav dalla GUI per il particolare in IIS.
1) Vai all'II.
2) Vai al rispettivo sito.
3) Apri "Handler Mappings"
4) Scorri verso il basso e seleziona il modulo WebDav. Fai clic destro su di esso ed eliminalo.

Nota: questo aggiornerà anche il tuo web.config dell'app web.


-1

L'applicazione client e l'applicazione server devono trovarsi nello stesso dominio, ad esempio:

client - localhost

server - localhost

e non :

client - localhost: 21234

server - localhost


2
Non la penso così. Lo scopo di creare un servizio è chiamare da un altro dominio
Ozan BAYRAM,

Stai pensando a una richiesta tra domini, che ti darà una risposta 200 dal server, ma il browser applicherà la sua regola "nessuna richiesta tra domini" e non accetterà la risposta. La domanda si riferisce a una risposta 405 "Metodo non consentito", un problema diverso.
Josh Noe,

CORS fornirà 405 "Metodo non consentito", ad esempio: URL richiesta: testapi.nottherealsite.com/api/Reporting/RunReport Metodo richiesta: OPZIONI Codice stato: 405 Metodo non consentito, leggi qui stackoverflow.com/questions/12458444/…
Lev K.,

Ti stai riferendo al problema CORS.
user3151766
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.