Żądanie wprowadzenia interfejsu API sieci Web generuje błąd Http 405 Niedozwolona metoda


134

Oto wywołanie PUTmetody w moim interfejsie API sieci Web - trzeci wiersz w metodzie (wywołuję interfejs API sieci Web z interfejsu ASP.NET MVC):

wprowadź opis obrazu tutaj

client.BaseAddressjest http://localhost/CallCOPAPI/.

Oto contactUri:

wprowadź opis obrazu tutaj

Oto contactUri.PathAndQuery:

wprowadź opis obrazu tutaj

I na koniec moja odpowiedź 405:

wprowadź opis obrazu tutaj

Oto plik WebApi.config w moim projekcie interfejsu API sieci 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);

Próbowałem zedrzeć ścieżkę, która prowadzi PutAsJsonAsyncdo string.Format("/api/department/{0}", department.Id)i string.Format("http://localhost/CallCOPAPI/api/department/{0}", department.Id)bez powodzenia.

Czy ktoś ma jakieś pomysły, dlaczego otrzymuję błąd 405?

AKTUALIZACJA

Zgodnie z żądaniem, oto mój kod kontrolera działu (opublikuję zarówno kod kontrolera działu dla mojego projektu frontendowego, jak i kod działu ApiController dla interfejsu WebAPI):

Kontroler działu 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 });
        }
    }
}

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

Czy nie powinieneś używać [HttpPut]przed definicją metody akcji? ( [HttpPost]i [HttpDelete]tam, gdzie jest to stosowne)
— Chris Pratt,

@ChrisPratt Żeby było jasne, masz na myśli [HttpPut]włączenie kontrolera WebAPI (ApiController), prawda? Ponieważ kontroler frontonu dla działu (metoda edycji) ma [HttpPost]atrybut.
— Mike Marks

1
@ChrisPratt ValuesController (ten, który jest dostarczany z szablonem WebAPI) nie ma [HttpPut]atrybutów itp. W metodach Put / Post / Delete ..
— Mike Marks

Tak, jestem pewien, że potrzebuje tych po stronie interfejsu API sieci Web. Osobiście zawsze używałem AttributeRouting do obsługi interfejsu API sieci Web, więc moje wspomnienia są trochę pobieżne.
— Chris Pratt,

Najwyraźniej chodziło o WebDAV ... Sprawdziłem lokalne IIS (funkcje systemu Windows), aby upewnić się, że nie jest zainstalowane i powiedziałem, że nie jest ... w każdym razie opublikowałem odpowiedź na to pytanie, po prostu usuwając moduł WebDAV w mojej sieci .config.
— Mike Marks

Odpowiedzi:


304

Więc sprawdziłem Funkcje systemu Windows, aby upewnić się, że nie mam zainstalowanej rzeczy o nazwie WebDAV, i powiedziałem, że nie. W każdym razie poszedłem dalej i umieściłem następujący plik w moim web.config (zarówno interfejs użytkownika, jak i WebAPI, dla pewności) i teraz działa. Włożyłem to do środka <system.webServer>.

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

Ponadto często wymagane jest dodanie następujących elementów do web.configw programach obsługi. Dzięki Babak

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

2
Haha… tak… Już miałem się poddać. Więc tak. WebDAV musi być włączony w Twoim applicationhost.config. Cieszę się, że to naprawiłeś.
— Aron,

9
Konieczne może być również dodanie tego:<handlers><remove name="WebDAV" />...
— Babak

14
Dodałem to tylko do mojego WebApi web.config i zadziałało.
— Fordy

Mimo że w IE10 działało dobrze nawet bez tej konfiguracji, musiałem zrobić tylko w WebApi web.config, aby działał w przeglądarce Chrome.
— Dennis R

1
Dzięki za odpowiedź na ten naprawdę irytujący problem. Dlaczego tak się dzieje w pierwszej kolejności?
— Scott Wilson,

23

WebDav-SchmebDav .. .. upewnij się, że poprawnie utworzyłeś adres URL z identyfikatorem. Nie wysyłaj jak http://www.fluff.com/api/Fluff?id=MyID , wysyłaj jak http://www.fluff.com/api/Fluff/MyID .

Na przykład.

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

{"Data":"1"}

To rozwalało mi jaja na małą wieczność, totalne zażenowanie.


3
Dodatkowy problem dla mnie: akcje PUT nie mogą wiązać danych z parametrami typu pierwotnego. Musiałem przesiąść się public int PutFluffColor(int Id, int colorCode)napublic int PutFluffColor(int Id, UpdateFluffColorModel model)
— Josh Noe

4
Chciałbym móc dwukrotnie głosować za tym głosem na WebDav-SchmebDav
— Noel,

1
po ponad 8 godzinach pracy dochodzimy do rozwiązania, każdy poleca zmianę web.config tak niesamowitą, że nikt nawet nie mówił o takiej możliwości.
— sairfan

22

Dodaj to do swojego web.config. Musisz powiedzieć usługom IIS, co PUT PATCH DELETEi co OPTIONSoznacza. I IHttpHandlerdo którego przywołać.

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

Sprawdź również, czy nie masz włączonego protokołu WebDAV.


Już to mam. Zakładam, że ma to zostać umieszczone w projekcie interfejsu API sieci Web, a nie w moim projekcie frontonu MVC, prawda?
— Mike Marks

Nie mam zainstalowanego WebDAV. Ponadto, czy twierdzisz, że powyższy kod web.config należy umieścić w pliku web.config projektu, który wykonuje wywołanie internetowego interfejsu API?
— Mike Marks

W rzeczywistości jest w obu plikach web.config ... :(
— Mike Marks

O nie ... Myślałem, że odwołujesz się do projektu internetowego interfejsu API z projektu MVC.
— Aron,

1
Czy możesz opublikować listę kodów DepartmentController? Wszystko. Problem leży w twoim projekcie Web API i nie wie, jak sobie z tym poradzić PUT, to właśnie oznacza 405. Sprawdź, czy działa GET, aby wykluczyć routing. PS. Spróbuj skopiować kod wklejany zamiast zrzutu ekranu. PPS, NIE UŻYWAJ Task.Result, w niektórych sytuacjach pojawią się niepowiązane problemy z wątkami. Zamiast tego po prostu zmień całą metodę na async await. Nie wspominając o tym, że tworzy synchroniczny, wielowątkowy blokowany kod (wolniejszy niż jednowątkowy).
— Aron,

14

Używam aplikacji ASP.NET MVC 5 w usługach IIS 8.5. Wypróbowałem wszystkie zamieszczone tutaj odmiany i tak web.configwygląda mój wygląd:

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

Nie mogłem odinstalować WebDav z mojego serwera, ponieważ nie miałem uprawnień administratora. Czasami otrzymywałem method not allowedpliki .css i .js. W końcu po powyższej konfiguracji wszystko zaczęło działać ponownie.


5

Udekorowanie jednego z parametrów działania za pomocą [FromBody] rozwiązało problem:

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

Jednak ASP.NET wywnioskowałby to poprawnie, gdyby w parametrze metody zastosowano obiekt złożony:

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

1

Inną przyczyną może być to, że nie używasz domyślnej nazwy zmiennej dla „id”, którą w rzeczywistości jest: id.


0

W moim przypadku błąd 405 został wywołany przez procedurę obsługi statycznej z powodu konfliktu trasy („api / images”) z folderem o tej samej nazwie („~ / images”).


0

Możesz ręcznie usunąć moduł webdav z GUI dla konkretnego w IIS.
1) Idź do IIs.
2) Przejdź do odpowiedniej witryny.
3) Otwórz „Handler Mappings”
4) Przewiń w dół i wybierz moduł WebDav. Kliknij go prawym przyciskiem myszy i usuń.

Uwaga: spowoduje to również zaktualizowanie pliku web.config aplikacji internetowej.


-1

Twoja aplikacja kliencka i aplikacja serwerowa muszą znajdować się w tej samej domenie, na przykład:

klient - localhost

serwer - localhost

i nie :

klient - localhost: 21234

serwer - localhost


2
Nie sądzę. Celem stworzenia usługi jest dzwonienie z innej domeny
— Ozan BAYRAM

Myślisz o żądaniu międzydomenowym, które da 200 odpowiedzi z serwera, ale przeglądarka wymusi swoją regułę „brak żądań między domenami” i nie zaakceptuje odpowiedzi. Pytanie dotyczy odpowiedzi 405 „Metoda niedozwolona”, innego problemu.
— Josh Noe

CORS poda 405 „Metoda niedozwolona ”, na przykład: Adres URL żądania: testapi.nottherealsite.com/api/Reporting/RunReport Metoda żądania: OPCJE Kod stanu: 405 Metoda niedozwolona, przeczytaj tutaj stackoverflow.com/questions/12458444/…
— Lev K.

Masz na myśli problem z CORS.
— user3151766
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.