Запит на поставку веб-API генерує помилку методу Http 405, недопустима


134

Ось заклик до PUTметоду в моєму веб-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?

ОНОВЛЕННЯ

Відповідно до запиту, ось мій код контролера відділення (я опублікую як код контролера відділення для мого переднього проекту, так і код 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 для речей Web API, тому мій спогад трохи схематичний.
— Кріс Пратт

Мабуть, це була річ WebDAV. Я перевірив свій локальний IIS (Особливості Windows), щоб переконатися, що він не встановлений, і він сказав, що це не так ... все одно я опублікував відповідь на це, в основному видаливши модуль WebDAV всередині моєї веб-сторінки .config.
— Майк Маркс

Відповіді:


304

Отже, я перевірив Особливості 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 і воно спрацювало.
— Форді

Незважаючи на те, що в 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"}

Це кидало мої кульки на малу вічність, тотальне збентеження.


3
Додатковий кульовий шар для мене: дії PUT не можуть прив’язувати дані до параметрів примітивного типу. Мені довелося перейти public int PutFluffColor(int Id, int colorCode)наpublic int PutFluffColor(int Id, UpdateFluffColorModel model)
— Джош Ное

4
Хочеться, щоб я міг схвалити це двічі за WebDav-SchmebDav
— Ноель

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 на передньому кінці, правда?
— Майк Маркс

У мене не встановлено WebDAV. Крім того, ви хочете сказати, що вищевказаний код web.config повинен бути розміщений у web.config проекту, який здійснює виклик у веб-API?
— Майк Маркс

Це насправді в обох web.configs ... :(
— Майк Маркс

О ні ... Я думав, що ви посилаєтесь на проект Web API з проекту MVC.
— Арон

1
Чи можете ви опублікувати список кодів відділу управління? Все це. Проблема полягає у вашому проекті Web API, і він не знає, як впоратися PUT, саме це означає 405. Переконайтесь, що GET працює, лише щоб виключити маршрутизацію. PS. Спробуйте скопіювати код вставлення, а не скріншот. PPS, НЕ ВИКОРИСТОВУЙТЕ Task.Result, у певних ситуаціях виникатимуть непов’язані проблеми з ниткою. Просто перетворіть весь метод на асинхронізацію, що чекає замість цього. Не кажучи вже про те, що він створює синхронний, багатопотоковий заблокований код (повільніше, ніж однопотоковий).
— Арон

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) Відкрийте "Зображення обробника"
4) Прокрутіть вниз та виберіть модуль WebDav. Клацніть правою кнопкою миші та видаліть її.

Примітка. Це також оновить вашу web.config веб-програми.


-1

Ваша клієнтська програма та серверна програма повинні бути в одному домені, наприклад:

клієнт - localhost

сервер - localhost

і ні :

клієнт - localhost: 21234

сервер - localhost


2
Я не думаю, що так. Метою створення сервісу є дзвінок з іншого домену
— Ozan BAYRAM

Ви думаєте про крос-доменний запит, який дасть вам 200 відповідей від сервера, але браузер буде застосовувати своє правило "без запитів між доменами" і не прийме відповідь. Питання посилається на відповідь 405 "Метод не дозволений", інше питання.
— Джош Ное

CORS видасть 405 "Метод не дозволений", наприклад: URL-адреса запиту: testapi.nottherealsite.com/api/Reporting/RunReport Метод запиту: ВАРІАНТИ Код статусу: 405 Метод не дозволений, будь ласка, прочитайте тут stackoverflow.com/questions/12458444/…
— Лев К.

Ви посилаєтесь на проблему CORS.
— користувач3151766
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.