Permintaan Put API Web menghasilkan kesalahan Metode Http 405 Tidak Diizinkan

134

Inilah panggilan ke PUTmetode pada API Web saya - baris ketiga dalam metode (Saya memanggil API Web dari ujung depan ASP.NET MVC):

masukkan deskripsi gambar di sini

client.BaseAddressadalah http://localhost/CallCOPAPI/.

Inilah contactUri:

masukkan deskripsi gambar di sini

Inilah contactUri.PathAndQuery:

masukkan deskripsi gambar di sini

Dan akhirnya, inilah tanggapan 405 saya:

masukkan deskripsi gambar di sini

Inilah WebApi.config dalam proyek API Web saya:

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

Saya sudah mencoba melucuti menuruni jalan yang akan masuk ke PutAsJsonAsyncke string.Format("/api/department/{0}", department.Id)dan string.Format("http://localhost/CallCOPAPI/api/department/{0}", department.Id)tidak berhasil.

Adakah yang tahu mengapa saya mendapatkan error 405?

MEMPERBARUI

Sesuai permintaan, inilah kode pengontrol Departemen saya (saya akan memposting kode pengontrol Departemen untuk proyek front end saya, serta kode Departemen ApiController untuk WebAPI):

Pengontrol Departemen 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 Departemen 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);
        }
    }
}
Mike Marks
sumber
Bukankah seharusnya Anda menggunakan [HttpPut]sebelum definisi metode tindakan? ( [HttpPost]dan [HttpDelete]jika perlu juga)
Chris Pratt
@ ChrisPratt Agar lebih jelas, maksud Anda memakai [HttpPut]pengontrol WebAPI (ApiController), kan? Karena pengontrol ujung depan untuk Departemen (metode Edit) memiliki [HttpPost]atribut.
Mike Marks
1
@ChrisPratt ValuesController (yang dilengkapi dengan templat WebAPI) tidak memiliki [HttpPut], dll. Atribut pada metode Put / Post / Delete ..
Mike Marks
Ya, saya cukup yakin ini membutuhkan yang ada di sisi API Web. Secara pribadi, saya selalu menggunakan AttributeRouting untuk hal-hal API Web, jadi ingatan saya sedikit samar.
Chris Pratt
Rupanya itu adalah hal WebDAV .. Saya memeriksa IIS lokal saya (Fitur Windows) untuk memastikan tidak diinstal dan katanya tidak ... lagian saya memposting jawaban untuk ini, pada dasarnya menghapus modul WebDAV di dalam web saya .config.
Mike Marks

Jawaban:

304

Jadi, saya memeriksa Fitur Windows untuk memastikan saya tidak memiliki hal yang disebut WebDAV diinstal, dan katanya saya tidak. Bagaimanapun, saya pergi ke depan dan menempatkan yang berikut di web.config saya (baik front end dan WebAPI, hanya untuk memastikan), dan itu berfungsi sekarang. Saya menempatkan ini di dalam <system.webServer>.

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

Selain itu, sering kali diperlukan untuk menambahkan yang berikut ke web.configdalam penangan. Terima kasih untuk Babak

<handlers>
    <remove name="WebDAV" />
    ...
</handlers>
Mike Marks
sumber
2
Haha ... ya ... aku hampir menyerah. Jadi ya. WebDAV harus diaktifkan di applicationhost.config. Senang Anda sudah memperbaikinya.
Aron
9
Anda mungkin juga perlu menambahkan ini:<handlers><remove name="WebDAV" />...
Babak
14
Menambahkan ini hanya ke web.pi webApi saya dan berhasil.
Fordy
Meskipun di IE10 bekerja dengan baik bahkan tanpa konfigurasi ini, saya harus melakukan hanya di WebApi web.config untuk membuatnya berfungsi di browser Chrome.
Dennis R
1
Terima kasih atas jawaban untuk masalah yang sangat menjengkelkan ini. Mengapa ini terjadi?
Scott Wilson
23

WebDav-SchmebDav ... ..pastikan Anda membuat url dengan ID dengan benar. Jangan kirim seperti http://www.fluff.com/api/Fluff?id=MyID , kirimkan seperti http://www.fluff.com/api/Fluff/MyID .

Misalnya.

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

{"Data":"1"}

Ini membuatku kesal karena kekekalan kecil, benar-benar memalukan.

Molibar
sumber
3
Ball buster tambahan untuk saya: Tindakan PUT tidak dapat mengikat data ke parameter tipe primitif. Saya harus pindah public int PutFluffColor(int Id, int colorCode)kepublic int PutFluffColor(int Id, UpdateFluffColorModel model)
Josh Noe
4
Seandainya saya bisa melakukan upvote dua kali untuk WebDav-SchmebDav
Noel
1
setelah lebih dari 8 jam kerja mencapai solusi, setiap orang merekomendasikan perubahan web.config begitu luar biasa bahkan tidak ada yang membicarakan tentang kemungkinan ini.
sairfan
22

Tambahkan ini ke web.config. Anda perlu memberi tahu IIS apa PUT PATCH DELETEdan OPTIONSartinya. Dan yang IHttpHandlerharus dipanggil.

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

Periksa juga Anda tidak mengaktifkan WebDAV.

Aron
sumber
Saya sudah memilikinya. Saya berasumsi ini akan dimasukkan ke dalam proyek Web API, bukan proyek MVC front end saya, kan?
Mike Marks
Saya tidak menginstal WebDAV. Selain itu, apakah Anda mengatakan bahwa kode web.config di atas perlu ditempatkan di web.config dari proyek yang membuat panggilan ke Web API?
Mike Marks
Ini sebenarnya di kedua web.configs ... :(
Mike Marks
Oh tidak ... Saya pikir Anda mereferensikan proyek Web API dari proyek MVC.
Aron
1
Bisakah Anda memposting daftar kode DepartmentController? Semua itu. Masalahnya terletak pada proyek API Web Anda, dan tidak tahu bagaimana cara menanganinya PUT, itulah yang dimaksud 405. Periksa apakah GET berfungsi, hanya untuk mengesampingkan perutean. PS. Cobalah untuk menyalin kode tempel daripada tangkapan layar. PPS, JANGAN GUNAKAN Task.Result, Anda akan mendapatkan masalah threading yang tidak terkait dalam situasi tertentu. Alih-alih, ubah seluruh metode menjadi async. Belum lagi itu menciptakan sinkron, kode diblokir multithreaded (lebih lambat dari satu threaded)
Aron
14

Saya menjalankan aplikasi ASP.NET MVC 5 di IIS 8.5. Saya mencoba semua variasi yang diposting di sini, dan inilah yang web.configterlihat seperti:

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

Saya tidak dapat menghapus WebDav dari Server saya karena saya tidak memiliki hak admin. Juga, kadang-kadang saya mendapatkan file method not allowed.css dan .js. Pada akhirnya, dengan konfigurasi di atas mengatur semuanya mulai berfungsi kembali.

jpgrassi
sumber
5

Dekorasi salah satu aksi params dengan [FromBody] memecahkan masalah bagi saya:

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

Namun ASP.NET akan menyimpulkannya dengan benar jika objek kompleks digunakan dalam parameter metode:

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

Penyebab lain dari hal ini adalah jika Anda tidak menggunakan nama variabel default untuk "id" yang sebenarnya: id.

Adam Levitt
sumber
0

Dalam kasus saya kesalahan 405 dipanggil oleh pengendali statis karena rute ("api / gambar") yang bertentangan dengan folder dengan nama yang sama ("~ / gambar").

Petr Šugar
sumber
0

Anda dapat menghapus modul webdav secara manual dari GUI untuk khususnya di IIS.
1) Pergi ke IIs.
2) Mendapat situs masing-masing.
3) Buka "Pemetaan Handler"
4) Gulir ke bawah dan pilih modul WebDav. Klik kanan padanya dan hapus.

Catatan: ini juga akan memperbarui web.config Anda dari aplikasi web.

Naveen Kumar GC
sumber
-1

Aplikasi klien dan aplikasi server Anda harus di bawah domain yang sama, misalnya:

client - localhost

server - localhost

dan tidak :

client - localhost: 21234

server - localhost

Lev K.
sumber
2
Saya rasa tidak. Tujuan membuat layanan adalah untuk menelepon dari domain lain
Ozan BAYRAM
Anda memikirkan permintaan lintas domain, yang akan memberi Anda 200 tanggapan dari server, tetapi peramban akan menegakkan aturan "tidak ada permintaan lintas domain" dan tidak menerima respons. Pertanyaannya mengacu pada respons 405 "Metode Tidak Diizinkan", masalah yang berbeda.
Josh Noe
CORS akan memberikan 405 "Metode Tidak Diizinkan", misalnya: URL Permintaan: testapi.nottherealsite.com/api/Reporting/RunReport Request Method: OPTIONS Kode Status: 405 Metode Tidak Diizinkan silakan baca di sini stackoverflow.com/questions/12458444/…
Lev K.
Anda mengacu pada masalah CORS.
user3151766