La demande de mise de l'API Web génère une erreur Méthode Http 405 non autorisée


134

Voici l'appel à la PUTméthode sur mon API Web - la troisième ligne de la méthode ( j'appelle l' API Web à partir d'un frontal ASP.NET MVC):

entrez la description de l'image ici

client.BaseAddressest http://localhost/CallCOPAPI/.

Voici contactUri:

entrez la description de l'image ici

Voici contactUri.PathAndQuery:

entrez la description de l'image ici

Et enfin, voici ma réponse 405:

entrez la description de l'image ici

Voici le WebApi.config dans mon projet d'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);

J'ai essayé de décapage sur le chemin qui est passé dans PutAsJsonAsyncà string.Format("/api/department/{0}", department.Id)et string.Format("http://localhost/CallCOPAPI/api/department/{0}", department.Id)sans chance.

Quelqu'un a-t-il une idée de la raison pour laquelle j'obtiens l'erreur 405?

METTRE À JOUR

Selon la demande, voici mon code de contrôleur de département (je publierai à la fois le code de contrôleur de département pour mon projet frontal, ainsi que le code ApiController de département pour WebAPI):

Contrôleur de département frontal

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

Service API Web 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);
        }
    }
}

Ne devriez-vous pas utiliser [HttpPut]avant la définition de la méthode d'action? ( [HttpPost]et le [HttpDelete]cas échéant également)
Chris Pratt

@ChrisPratt Pour être clair, vous voulez dire mettre [HttpPut]sur le contrôleur WebAPI (ApiController), non? Parce que le contrôleur frontal pour Department (méthode Edit) a un [HttpPost]attribut.
Mike Marks

1
@ChrisPratt Le ValuesController (celui qui vient avec le modèle WebAPI) n'a pas [HttpPut], etc. d'attributs sur les méthodes Put / Post / Delete ..
Mike marque

Oui, je suis raisonnablement sûr qu'il en a besoin du côté de l'API Web. Personnellement, j'ai toujours utilisé AttributeRouting pour les API Web, donc mon souvenir est un peu sommaire.
Chris Pratt

Apparemment, c'était le truc WebDAV .. J'ai vérifié mon IIS local (Windows Features) pour m'assurer qu'il n'était pas installé et il a dit que ce n'était pas le cas ... de toute façon j'ai publié une réponse à cela, en supprimant essentiellement le module WebDAV à l'intérieur de mon site Web .config.
Mike Marks

Réponses:


304

Donc, j'ai vérifié les fonctionnalités de Windows pour m'assurer que je n'avais pas installé cette chose appelée WebDAV, et cela a dit que je ne l'avais pas. Quoi qu'il en soit, je suis allé de l'avant et j'ai placé ce qui suit dans mon web.config (à la fois frontal et WebAPI, juste pour être sûr), et cela fonctionne maintenant. J'ai placé ça à l'intérieur <system.webServer>.

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

De plus, il est souvent nécessaire d'ajouter les éléments suivants web.configdans les gestionnaires. Merci à Babak

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

2
Haha ... ouais ... J'étais sur le point d'abandonner. Donc voilà. WebDAV doit avoir été activé dans votre applicationhost.config. Heureux que vous ayez résolu le problème.
Aron

9
Vous devrez peut-être également ajouter ceci:<handlers><remove name="WebDAV" />...
Babak

14
Ajouté ceci uniquement à mon web.config WebApi et cela a fonctionné.
Fordy

Même si dans IE10 cela fonctionnait bien même sans cette configuration, je devais le faire uniquement dans WebApi web.config pour le faire fonctionner dans le navigateur Chrome.
Dennis R

1
Merci pour la réponse à ce problème vraiment ennuyeux. Pourquoi cela se produit-il en premier lieu?
Scott Wilson

23

WebDav-SchmebDav .. .. assurez-vous de créer correctement l'url avec l'ID. Ne l'envoyez pas comme http://www.fluff.com/api/Fluff?id=MyID , envoyez-le comme http://www.fluff.com/api/Fluff/MyID .

Par exemple.

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

{"Data":"1"}

Cela m'a brisé les couilles pendant une petite éternité, un embarras total.


3
Une balle supplémentaire pour moi: les actions PUT ne peuvent pas lier des données à des paramètres de type primitif. Je devais changer public int PutFluffColor(int Id, int colorCode)pourpublic int PutFluffColor(int Id, UpdateFluffColorModel model)
Josh Noe

4
J'aimerais pouvoir voter deux fois pour le WebDav-SchmebDav
Noel

1
après plus de 8 heures de travail pour arriver à la solution, tout le monde recommande que web.config change son si étonnant qu'aucun organisme n'a même pas parlé de cette possibilité.
sairfan

22

Ajoutez ceci à votre web.config. Vous devez dire à IIS ce PUT PATCH DELETEque OPTIONSsignifie et ce que cela signifie. Et qui IHttpHandlerinvoquer.

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

Vérifiez également que WebDAV n'est pas activé.


J'ai déjà ça. Je suppose que cela doit être mis dans le projet Web API, pas dans mon projet MVC frontal, non?
Mike Marks

Je n'ai pas installé WebDAV. De plus, dites-vous que le code web.config ci-dessus doit être placé dans le web.config du projet qui effectue l'appel à l'API Web?
Mike Marks

C'est en fait dans les deux web.configs ... :(
Mike Marks

Oh non ... Je pensais que vous faisiez référence à un projet d'API Web à partir d'un projet MVC.
Aron

1
Pouvez-vous afficher la liste des codes du DepartmentController? Tout. Le problème réside dans votre projet d'API Web, et il ne sait pas comment gérer PUT, c'est ce que veut dire 405. Vérifiez que GET fonctionne, juste pour exclure le routage. PS. Essayez de copier le code collé plutôt que la capture d'écran. PPS, NE PAS UTILISER Task.Result, vous aurez des problèmes de filetage sans rapport dans certaines situations. Transformez simplement toute la méthode en attente asynchrone. Sans oublier qu'il crée du code bloqué synchrone et multithread (plus lent qu'un thread unique).
Aron

14

J'exécute une application ASP.NET MVC 5 sur IIS 8.5. J'ai essayé toutes les variantes publiées ici, et voici à quoi je web.configressemble:

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

Je n'ai pas pu désinstaller WebDav de mon serveur car je n'avais pas de privilèges d'administrateur. Aussi, parfois, j'obtenais les method not allowedfichiers .css et .js. En fin de compte, avec la configuration ci-dessus, tout a recommencé à fonctionner.


5

La décoration de l'un des paramètres d'action avec [FromBody] a résolu le problème pour moi:

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

Cependant, ASP.NET l'inférerait correctement si un objet complexe était utilisé dans le paramètre de méthode:

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

1

Une autre cause de ceci pourrait être si vous n'utilisez pas le nom de variable par défaut pour "id" qui est en fait: id.


0

Dans mon cas, l'erreur 405 a été invoquée par le gestionnaire statique en raison d'une route ("api / images") en conflit avec le dossier du même nom ("~ / images").


0

Vous pouvez supprimer manuellement le module webdav de l'interface graphique pour le particulier dans IIS.
1) Aller aux II.
2) Accédez au site respectif.
3) Ouvrez "Handler Mappings".
4) Faites défiler vers le bas et sélectionnez le module WebDav. Faites un clic droit dessus et supprimez-le.

Remarque: cela mettra également à jour votre web.config de l'application Web.


-1

Votre application cliente et votre application serveur doivent être sous le même domaine, par exemple:

client - localhost

serveur - localhost

et pas :

client - localhost: 21234

serveur - localhost


2
Je ne pense pas. Le but de la création d'un service est d'appeler depuis un autre domaine
Ozan BAYRAM

Vous pensez à une requête interdomaine, qui vous donnera une réponse 200 du serveur, mais le navigateur appliquera sa règle "pas de demandes interdomaines" et n'acceptera pas la réponse. La question fait référence à une réponse 405 "Méthode non autorisée", une question différente.
Josh Noe

CORS donnera 405 "Méthode non autorisée", par exemple: URL de requête: testapi.nottherealsite.com/api/Reporting/RunReport Méthode de requête: OPTIONS Code d'état: 405 Méthode non autorisée, veuillez lire ici stackoverflow.com/questions/12458444/...
Lev K.

Vous parlez du problème CORS.
user3151766
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.