คำขอใส่ API ของเว็บสร้างข้อผิดพลาด Http 405 Method Not Allowed


139

นี่คือการเรียกใช้PUTเมธอดบน Web API ของฉัน - บรรทัดที่สามในเมธอด (ฉันกำลังเรียก Web API จากส่วนหน้า ASP.NET MVC):

ป้อนคำอธิบายภาพที่นี่

client.BaseAddressคือhttp://localhost/CallCOPAPI/.

นี่คือcontactUri:

ป้อนคำอธิบายภาพที่นี่

นี่คือcontactUri.PathAndQuery:

ป้อนคำอธิบายภาพที่นี่

และสุดท้ายนี่คือคำตอบ 405 ของฉัน:

ป้อนคำอธิบายภาพที่นี่

นี่คือ WebApi.config ในโครงการ Web API ของฉัน:

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

ฉันพยายามทำลายเส้นทางที่ผ่านเข้าPutAsJsonAsyncมาstring.Format("/api/department/{0}", department.Id)และstring.Format("http://localhost/CallCOPAPI/api/department/{0}", department.Id)ไม่มีโชค

ใครมีความคิดว่าทำไมฉันถึงได้รับข้อผิดพลาด 405?

อัปเดต

ตามคำขอนี่คือรหัสตัวควบคุมแผนกของฉัน (ฉันจะโพสต์ทั้งรหัสตัวควบคุมแผนกสำหรับโปรเจ็กต์ส่วนหน้าของฉันตลอดจนรหัส Department ApiController สำหรับ WebAPI):

Front End Department Controller

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 แผนก Web API

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

คุณไม่ควรใช้[HttpPut]ก่อนกำหนดวิธีการดำเนินการหรือไม่? ( [HttpPost]และ[HttpDelete]ตามความเหมาะสมเช่นกัน)
Chris Pratt

@ChrisPratt เพื่อความชัดเจนคุณหมายถึงใส่[HttpPut]ตัวควบคุม WebAPI (ApiController) ใช่ไหม? เนื่องจากตัวควบคุมส่วนหน้าสำหรับแผนก (วิธีการแก้ไข) มี[HttpPost]แอตทริบิวต์
Mike Marks

1
@ChrisPratt The ValuesController (อันที่มาพร้อมกับเทมเพลต WebAPI) ไม่มี[HttpPut]แอตทริบิวต์อื่น ๆ ในวิธีการใส่ / โพสต์ / ลบ ..
Mike Marks

ใช่ฉันค่อนข้างแน่ใจว่าจำเป็นต้องใช้สิ่งเหล่านี้ในฝั่ง Web API โดยส่วนตัวแล้วฉันเพิ่งใช้ AttributeRouting สำหรับ Web API มาโดยตลอดดังนั้นความทรงจำของฉันจึงค่อนข้างคร่าวๆ
Chris Pratt

เห็นได้ชัดว่ามันเป็นสิ่งที่ WebDAV .. ฉันตรวจสอบ IIS ในเครื่องของฉัน (คุณสมบัติของ Windows) เพื่อให้แน่ใจว่าไม่ได้ติดตั้งและมันก็บอกว่ามันไม่ใช่ ... อย่างไรก็ตามฉันโพสต์คำตอบสำหรับสิ่งนี้โดยทั่วไปจะลบโมดูล WebDAV ภายในเว็บ .config.
Mike Marks

คำตอบ:


310

ดังนั้นฉันจึงตรวจสอบคุณสมบัติของ Windows เพื่อให้แน่ใจว่าฉันไม่ได้ติดตั้งสิ่งนี้ที่เรียกว่า WebDAV และมันบอกว่าฉันไม่มี อย่างไรก็ตามฉันไปข้างหน้าและวางสิ่งต่อไปนี้ใน web.config ของฉัน (ทั้งส่วนหน้าและ WebAPI เพื่อความแน่ใจ) และมันก็ใช้งานได้ในขณะนี้ <system.webServer>ฉันวางอยู่ภายในนี้

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

นอกจากนี้มักจะต้องเพิ่มสิ่งต่อไปนี้web.configในตัวจัดการ ขอบคุณ Babak

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

2
ฮ่าฮ่า ... ใช่ ... ฉันกำลังจะยอมแพ้ ใช่ ต้องเปิดใช้งาน WebDAV ในapplicationhost.configไฟล์. ดีใจที่คุณแก้ไขได้
Aron

9
คุณอาจต้องเพิ่มสิ่งนี้ด้วย:<handlers><remove name="WebDAV" />...
Babak

14
ที่เพิ่มเข้ามานี้เพียงของฉัน WebAPI web.config และการทำงาน
Fordy

แม้ว่าใน IE10 จะทำงานได้ดีแม้ว่าจะไม่มีการกำหนดค่านี้ แต่ฉันต้องทำเฉพาะใน WebApi web.config เพื่อให้ใช้งานได้ในเบราว์เซอร์ Chrome
Dennis R

1
ขอบคุณสำหรับคำตอบสำหรับปัญหาที่น่ารำคาญนี้ เหตุใดจึงเกิดขึ้นตั้งแต่แรก
Scott Wilson

23

WebDav-SchmebDav .. .. ตรวจสอบให้แน่ใจว่าคุณสร้าง url ด้วย ID อย่างถูกต้อง อย่าส่งมันเหมือนhttp://www.fluff.com/api/Fluff?id=MyIDส่งมันเหมือนhttp://www.fluff.com/api/Fluff/MyID

เช่น.

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

{"Data":"1"}

นี่เป็นการจับลูกบอลของฉันไปชั่วนิรันดร์เล็กน้อยความอับอายโดยสิ้นเชิง


3
ball buster เพิ่มเติมสำหรับฉัน: การกระทำ PUT ไม่สามารถผูกข้อมูลกับพารามิเตอร์ประเภทดั้งเดิมได้ ฉันต้องเปลี่ยนpublic int PutFluffColor(int Id, int colorCode)เป็นpublic int PutFluffColor(int Id, UpdateFluffColorModel model)
Josh Noe

4
หวังว่าฉันจะโหวตให้สองครั้งนี้สำหรับ WebDav-SchmebDav
Noel

1
หลังจากใช้เวลานานกว่า 8 ชั่วโมงในการแก้ปัญหาทุกคนขอแนะนำให้ web.config เปลี่ยนแปลงสิ่งที่น่าทึ่งจนไม่มีตัวไหนไม่ได้พูดถึงความเป็นไปได้นี้
sairfan

22

เพิ่มสิ่งนี้ลงในweb.configไฟล์. คุณต้องบอก IIS ว่าเป็นอย่างไรPUT PATCH DELETEและOPTIONSหมายความว่าอย่างไร และสิ่งที่IHttpHandlerจะเรียก

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

ตรวจสอบด้วยว่าคุณไม่ได้เปิดใช้งาน WebDAV


ฉันมีสิ่งนั้นอยู่แล้ว ฉันสมมติว่าสิ่งนี้จะถูกใส่ในโครงการ Web API ไม่ใช่โครงการ MVC ส่วนหน้าของฉันใช่ไหม
Mike Marks

ฉันไม่ได้ติดตั้ง WebDAV นอกจากนี้คุณกำลังบอกว่าต้องวางโค้ด web.config ด้านบนไว้ใน web.config ของโปรเจ็กต์ที่เรียกใช้ Web API หรือไม่
Mike Marks

มีอยู่จริงใน web.configs ทั้งคู่ ... :(
Mike Marks

โอ้ไม่ ... ฉันคิดว่าคุณกำลังอ้างอิงโครงการ Web API จากโครงการ MVC
Aron

1
โพสต์รายการรหัสกรมควบคุมได้ไหม ทั้งหมดของมัน. ปัญหาอยู่ในโครงการ Web API ของคุณและไม่ทราบวิธีจัดการPUTนั่นคือความหมายของ 405 ตรวจสอบว่า GET ใช้งานได้เพียงเพื่อตัดการกำหนดเส้นทาง ปล. พยายามคัดลอกวางโค้ดแทนภาพหน้าจอ PPS อย่าใช้Task.Resultคุณจะได้รับปัญหาเธรดที่ไม่เกี่ยวข้องในบางสถานการณ์ เพียงแค่เปลี่ยนวิธีการทั้งหมดเป็น async รอแทน ไม่ต้องพูดถึงมันสร้างโค้ดที่บล็อกแบบซิงโครนัสแบบมัลติเธรด (ช้ากว่าเธรดเดี่ยว)
Aron

14

ฉันใช้งานแอปพลิเคชัน ASP.NET MVC 5 บน IIS 8.5 ฉันลองรูปแบบทั้งหมดที่โพสต์ไว้ที่นี่และนี่คือweb.configลักษณะของฉัน:

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

ฉันไม่สามารถถอนการติดตั้ง WebDav จากเซิร์ฟเวอร์ของฉันได้เนื่องจากฉันไม่มีสิทธิ์ระดับผู้ดูแลระบบ นอกจากนี้บางครั้งฉันก็ได้รับmethod not allowedไฟล์. css และ. js ในท้ายที่สุดด้วยการกำหนดค่าด้านบนการตั้งค่าทุกอย่างก็เริ่มทำงานอีกครั้ง


5

การตกแต่งพารามิเตอร์การกระทำอย่างใดอย่างหนึ่งด้วย [FromBody] ช่วยแก้ปัญหาให้ฉันได้:

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

อย่างไรก็ตาม ASP.NET จะอนุมานได้อย่างถูกต้องหากใช้วัตถุที่ซับซ้อนในพารามิเตอร์วิธีการ:

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

1

สาเหตุอีกประการหนึ่งอาจเกิดจากคุณไม่ได้ใช้ชื่อตัวแปรเริ่มต้นสำหรับ "id" ซึ่งจริงๆแล้วคือ id


0

ในกรณีของฉันข้อผิดพลาด 405 ถูกเรียกโดยตัวจัดการแบบคงที่เนื่องจากเส้นทาง ("api / images") ขัดแย้งกับโฟลเดอร์ที่มีชื่อเดียวกัน ("~ / images")


0

คุณสามารถลบโมดูล webdav ด้วยตนเองจาก GUI สำหรับเฉพาะใน IIS
1) ไปที่ II
2) ไปที่ไซต์นั้น ๆ
3) เปิด "Handler Mappings"
4) เลื่อนลงและเลือกโมดูล WebDav คลิกขวาและลบออก

หมายเหตุ: การดำเนินการนี้จะอัปเดต web.config ของเว็บแอปด้วย


-1

แอปพลิเคชันไคลเอนต์และแอปพลิเคชันเซิร์ฟเวอร์ของคุณต้องอยู่ภายใต้โดเมนเดียวกันตัวอย่างเช่น:

ไคลเอนต์ - localhost

เซิร์ฟเวอร์ - localhost

และไม่ :

ไคลเอนต์ - localhost: 21234

เซิร์ฟเวอร์ - localhost


2
ฉันไม่คิดอย่างนั้น จุดมุ่งหมายของการสร้างบริการคือการโทรจากโดเมนอื่น
Ozan BAYRAM

คุณกำลังนึกถึงคำขอข้ามโดเมนซึ่งจะให้การตอบกลับ 200 ครั้งจากเซิร์ฟเวอร์ แต่เบราว์เซอร์จะบังคับใช้กฎ "ไม่ขอข้ามโดเมน" และไม่ยอมรับการตอบกลับ คำถามนี้อ้างถึงคำตอบ 405 "วิธีที่ไม่อนุญาต" ซึ่งเป็นปัญหาที่แตกต่างกัน
Josh Noe

CORS จะให้ 405 "Method Not Allowed" ตัวอย่างเช่น Request URL: testapi.nottherealsite.com/api/Reporting/RunReport Request Method: OPTIONS Status Code: 405 Method Not Allowed โปรดอ่านที่นี่stackoverflow.com/questions/12458444/…
Lev K.

คุณกำลังอ้างถึงปัญหา CORS
user3151766
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.