Запрос на поставку веб-API генерирует ошибку Http 405 Method Not Allowed

135

Вот вызов PUTметода в моем веб-API - третья строка метода (я вызываю веб-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?

ОБНОВИТЬ

В соответствии с запросом, вот мой код контроллера Отдела (я опубликую как код контроллера Отдела для моего внешнего интерфейса, так и код Отдела ApiController для WebAPI):

Контроллер отдела переднего плана

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

Отдел веб-API 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);
        }
    }
}
Майк Маркс
источник
Разве вы не должны использовать [HttpPut]до определения метода действия? ( [HttpPost]и, [HttpDelete]где это уместно, тоже)
Крис Пратт
@ChrisPratt Для ясности, вы имеете в виду, что поставили [HttpPut]контроллер WebAPI (ApiController), верно? Поскольку внешний контроллер для отдела (метод редактирования) имеет [HttpPost]атрибут.
Майк Маркс,
1
@ChrisPratt ValuesController (тот, который поставляется с шаблоном WebAPI) не имеет [HttpPut]атрибутов и т. Д. В методах Put / Post / Delete ..
Майк Маркс,
Да, я вполне уверен, что ему нужны те, которые работают на стороне веб-API. Лично я всегда использовал AttributeRouting для веб-API, поэтому мои воспоминания немного отрывочны.
Крис Пратт
По-видимому, это была вещь WebDAV ... Я проверил свой локальный IIS (функции Windows), чтобы убедиться, что он не установлен, и он сказал, что это не так ... в любом случае я опубликовал ответ на это, в основном удалив модуль WebDAV из своей сети .config.
Майк Маркс

Ответы:

305

Итак, я проверил возможности Windows, чтобы убедиться, что у меня не установлена ​​эта вещь, называемая WebDAV, и она сказала, что у меня ее нет. В любом случае, я пошел дальше и поместил следующее в свой web.config (как интерфейс, так и WebAPI, на всякий случай), и теперь он работает. Я поместил это внутри <system.webServer>.

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

Кроме того, часто требуется добавить следующее web.configв обработчики. Благодаря Бабаку

<handlers>
    <remove name="WebDAV" />
    ...
</handlers>
Майк Маркс
источник
2
Хаха ... да ... Я собирался сдаться. Так что да. WebDAV должен быть включен в вашем applicationhost.config. Рад, что ты это починил.
Арон
9
Вам также может понадобиться добавить это:<handlers><remove name="WebDAV" />...
Бабак
14
Добавил это только в мой WebApi web.config и все заработало.
Fordy
Несмотря на то, что в IE10 он работал нормально даже без этого конфига, мне пришлось сделать только в WebApi web.config, чтобы он работал в браузере Chrome.
Денис Р
1
Спасибо за ответ на эту действительно досадную проблему. Почему это вообще происходит?
Скотт Уилсон
23

WebDav-SchmebDav .. .. убедитесь, что вы правильно создали URL с идентификатором. Не отправляйте его как 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"}

Это разрушало мои яйца в течение небольшой вечности, полного смущения.

Molibar
источник
3
Дополнительный шаровый удар для меня: действия PUT не могут привязать данные к параметрам примитивного типа. Я должен был измениться public int PutFluffColor(int Id, int colorCode)наpublic int PutFluffColor(int Id, UpdateFluffColorModel model)
Джош Но
4
Жаль, что я не смогу дважды проголосовать за WebDav-SchmebDav
Ноэль
1
после того, как более чем 8 часов работы доходят до решения, каждый рекомендует web.config, так что он так удивительно, что никто даже не говорил об этой возможности.
сайфан
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.

Aron
источник
У меня уже есть это. Я предполагаю, что это нужно поместить в проект веб-API, а не в мой проект внешнего интерфейса MVC, верно?
Майк Маркс
У меня не установлен WebDAV. Кроме того, вы говорите, что приведенный выше код web.config необходимо поместить в web.config проекта, который выполняет вызов веб-API?
Майк Маркс
Это вообще-то в обоих файлах web.configs ... :(
Майк Маркс
О нет ... Я думал, вы ссылаетесь на проект веб-API из проекта MVC.
Aron
1
Можете ли вы опубликовать листинг кода DepartmentController? Все это. Проблема заключается в вашем проекте Web API, и он не знает, как с этим справиться PUT, вот что означает 405. Убедитесь, что GET работает, чтобы исключить маршрутизацию. PS. Попробуйте скопировать код вставки, а не скриншот. PPS, НЕ ИСПОЛЬЗУЙТЕ Task.Result, вы получите не связанные с этим проблемы с потоками в определенных ситуациях. Вместо этого просто превратите весь метод в async await. Не говоря уже о том, что он создает синхронный многопоточный заблокированный код (медленнее, чем однопоточный).
Арон
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. В итоге с настройкой выше все снова заработало.

jpgrassi
источник
5

Добавление в один из параметров действия [FromBody] решило проблему для меня:

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

Однако ASP.NET сделает это правильно, если в параметре метода использовался сложный объект:

public async Task<IHttpActionResult> UpdateEntry(string id, MyEntry entry)
Владимiръ
источник
1

Другой причиной этого может быть то, что вы не используете имя переменной по умолчанию для «id», которое на самом деле: id.

Адам Левитт
источник
0

В моем случае ошибка 405 была вызвана статическим обработчиком из-за конфликта маршрута ("api / images") с одноименной папкой ("~ / images").

Петр Шугар
источник
0

Вы можете удалить модуль webdav вручную из GUI для конкретного в IIS.
1) Перейти к II.
2) Перейдите на соответствующий сайт.
3) Откройте «Сопоставления обработчиков»
4) Прокрутите вниз и выберите модуль WebDav. Щелкните его правой кнопкой мыши и удалите.

Примечание: это также обновит ваш web.config веб-приложения.

Навин Кумар GC
источник
-1

Ваше клиентское приложение и серверное приложение должны находиться в одном домене, например:

клиент - локальный хост

сервер - localhost

и нет :

клиент - localhost: 21234

сервер - localhost

Лев К.
источник
2
Я так не думаю. Целью создания сервиса является звонок с другого домена
Ozan BAYRAM
Вы думаете о междоменном запросе, который даст вам ответ 200 от сервера, но браузер будет применять свое правило «без междоменных запросов» и не примет ответ. Вопрос касается ответа 405 «Метод не разрешен», другой вопрос.
Джош Ной
CORS выдаст 405 «Метод не разрешен», например: URL-адрес запроса: testapi.nottherealsite.com/api/Reporting/RunReport. Метод запроса: ОПЦИИ Код состояния: 405 Метод не разрешен. Пожалуйста, прочтите здесь stackoverflow.com/questions/12458444/…
Лев К.
Вы имеете в виду проблему CORS.
user3151766