Запит на поставку веб-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.