Було знайдено кілька дій, які відповідають запиту в Web Api


243

Я продовжую отримувати цю помилку, коли намагаюся використовувати 2 "Отримати" методи

Було знайдено кілька дій, які відповідають запиту: webapi

Я дивився навколо інших подібних питань щодо цього в стеці, але не розумію.

У мене є 2 різних імена та використовується атрибут "HttpGet"

[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}

Відповіді:


485

Мапа маршруту, мабуть, приблизно така:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });

Але для того, щоб мати кілька дій з одним методом http, вам потрібно надати webapi більше інформації по маршруту:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });

Зауважте, що routeTemplate тепер включає дію. Більше інформації тут: http://www.asp.net/web-api/overview/web-api-routing-and-action/routing-in-aspnet-web-api

Оновлення:

Добре, тепер, коли я думаю, що я розумію, що ти шукаєш тут, - це ще одна справа:

Можливо, вам не потрібен параметр URL-адреси дії, і слід описати вміст, який ви шукаєте, по-іншому. Оскільки ви говорите, що методи повертають дані з тієї ж сутності, тоді просто дозвольте параметрам робити опис для вас.

Наприклад, ваші два методи можна перетворити на:

public HttpResponseMessage Get()
{
    return null;
}

public HttpResponseMessage Get(MyVm vm)
{
    return null;
}

Які дані ви передаєте в об’єкт MyVm? Якщо ви можете просто передати змінні через URI, я б запропонував пройти цей маршрут. В іншому випадку вам потрібно буде надіслати об’єкт в тілі запиту, і це не дуже HTTP з вас, коли ви робите GET (він працює, однак, просто використовуйте [FromBody] перед MyVm).

Сподіваємось, це ілюструє, що ви можете мати кілька методів GET в одному контролері, не використовуючи ім'я дії або навіть атрибут [HttpGet].


Чи є якісь переваги робити так чи інакше? Якщо я роблю вторинну, чи потрібно просто робити дію Http на кожному методі? Це велика нічия назад?
chobo2

3
Від того, чи буде одна перевага перед іншою, дійсно залежить від вашого проекту. Якщо ви будуєте RESTful api, тоді вам потрібно використовувати конвенції HTTP (GET, POST, PUT, DELETE ...). У цьому випадку перший блок маршрутизаційного коду - це шлях, але вам потрібен інший контролер для кожної сутності, яку ви виставляєте через api. Виходячи з назв вашого методу, я гадаю, що це не так, тому використовуйте більш описову маршрутизацію. Коли ваш маршрут включає в себе дію, вам потрібно буде явно поставити атрибут http для кожного методу.
Джед

1
@ chobo2 Чому б не просто використовувати методи, які відповідно названі в контролері? GetSummary (MyVm WM) і GetDetails ()
Джед

1
Дякую за вашу відповідь, я просто допомогла мені зрозуміти, чому дозвіл маршруту не працює, хоча обидві мої дії мали різні назви. Я дуже розгублений, чому це не просто поведінка за замовчуванням (тобто чому шаблон шаблону маршруту за замовчуванням у webapiconfig.cs не входить "{action}")!
Техас Шарма

1
@bruno при використанні областей ви можете також додати «адміністратор» конкретні інтерфейси API , як це в AdminAreaRegistration stackoverflow.com/a/9849011/16940
Simon_Weaver

67

Оновлення від Web API 2.

За допомогою цього конфігурації API у вашому файлі WebApiConfig.cs:

public static void Register(HttpConfiguration config)
{
    //// Web API routes
    config.MapHttpAttributeRoutes(); //Don't miss this

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = System.Web.Http.RouteParameter.Optional }
    );
}

Ви можете маршрутизувати наш контролер так:

[Route("api/ControllerName/Summary")]
[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    rturn null;
}

[Route("api/ControllerName/FullDetails")]
[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}

Де ControllerName - це назва вашого контролера (без "контролера"). Це дозволить вам отримувати кожну дію за допомогою детального маршруту, описаного вище.

Для подальшого читання: http://www.asp.net/web-api/overview/web-api-routing-and-action/attribute-routing-in-web-api-2


Мені дуже сподобалося це рішення. У мого маршруту за замовчуванням все ще той самий, і у мене є виняток для виключень
Леандро Де Мелло Фагундес,

Ви також можете зіставити параметри в URL EX: [Route ("api / ControllerName / Summary / {vm}")]
nulltron

15

У веб-API (за замовчуванням) методи вибираються на основі комбінації методу HTTP та значень маршруту .

MyVmвиглядає як складний об'єкт, зчитуваний форматером з тіла, тому у вас є два однакові методи з точки зору даних маршруту (оскільки жоден з них не має жодних параметрів маршруту), що унеможливлює диспетчер ( IHttpActionSelector) співставити відповідний .

Щоб вирішити неоднозначність, вам потрібно відрізняти їх за запитом або параметром маршруту.


14

Після багатого пошуку в Інтернеті та спроби знайти найбільш підходящу форму для маршрутизації карти, якщо знайшли наступне

config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id =RouteParameter.Optional }, new { id = @"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");

Ці відображення застосовуються як до відображення назв дії, так і до базової конвенції http (GET, POST, PUT, DELETE)


9
Для мене це спрацювало, але лише змінивши порядок маршрутів у конфігурації маршруту, щоб той, що діяв, з’явився першим
Фредрік Столпе

саме тут важливий порядок
AT


5

Без використання дій можливі варіанти:

  1. перемістіть один із методів до іншого контролера, щоб вони не стикалися.

  2. скористайтеся лише одним методом, який приймає парам, і якщо це недійсне, виклик іншого методу з вашого коду.


Це може бути рішенням, але не оптимальним, все одно +1 з моєї сторони :)
satish kumar V

4

Це рішення спрацювало на мене.

Будь ласка, поставте Route2 першим у WebApiConfig. Також додайте HttpGet і HttpPost перед кожним методом і додайте в URL-адресу ім'я контролера та ім'я методу.

WebApiConfig =>

config.Routes.MapHttpRoute(
           name: "MapByAction",
           routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional });
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional });

Контролер =>

public class ValuesController : ApiController
{

    [HttpPost]
    public string GetCustomer([FromBody] RequestModel req)
    {
        return "Customer";
    }

    [HttpPost]
    public string GetCustomerList([FromBody] RequestModel req)
    {
        return "Customer List";
    }
}

URL =>

http://localhost:7050/api/Values/GetCustomer

http://localhost:7050/api/Values/GetCustomerList

4

Це відповідь для всіх, хто знає, що все правильно і перевірив 50 разів .....

Переконайтеся, що ви не раз переглядаєте RouteConfig.cs.

Файл, який ви бажаєте відредагувати, названий WebApiConfig.cs

Крім того, це, мабуть, має виглядати саме так:

using System.Web.Http;

namespace My.Epic.Website
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
          config.MapHttpAttributeRoutes();

          // api/Country/WithStates
          config.Routes.MapHttpRoute(
            name: "ControllerAndActionOnly",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { },
            constraints: new { action = @"^[a-zA-Z]+([\s][a-zA-Z]+)*$" });

          config.Routes.MapHttpRoute(
            name: "DefaultActionApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
          );
    }
    }
}

Я міг би врятувати себе близько 3 годин.


1
Дякую, ти врятував мене близько 3 годин
geedubb

3

Я виявив, що коли у мене є два методи Get, один параметр і один зі складним типом як параметр, я отримав ту саму помилку. Я вирішив це, додавши фіктивний параметр типу int, названий Id, як мій перший параметр, а потім мій параметр складного типу. Потім я додав параметр складного типу до шаблону маршруту. Наступне працювало для мене.

Спочатку отримайте:

public IEnumerable<SearchItem> Get()
{
...
}

Друге отримання:

public IEnumerable<SearchItem> Get(int id, [FromUri] List<string> layers)
{
...
}

WebApiConfig:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}/{layers}",
    defaults: new { id = RouteParameter.Optional, layers RouteParameter.Optional }
);

3

Це можливо завдяки використанню контролера MVC замість контролера Web API. Перевірте простір імен у контролері Web API, воно має бути наступним

using System.Net;
using System.Net.Http;
using System.Web.Http;

Якщо простір імен наведено нижче, то при виклику методу веб-api-контролера подається вище помилка

using System.Web;
using System.Web.Mvc;

2

Перевірте, чи є у вас два способи, які мають різну назву та однакові параметри.

Якщо так, видаліть будь-який із методів і спробуйте.


2

Я натрапив на цю проблему, намагаючись розширити свої контролери WebAPI додатковими діями.

Припустимо, ви б мали

public IEnumerable<string> Get()
{
    return this.Repository.GetAll();
}

[HttpGet]
public void ReSeed()
{
    // Your custom action here
}

Зараз існує два способи, які задовольняють запит на / api / контролер, який запускає проблему, описану TS.

Я не хотів додавати "фіктивних" параметрів до своїх додаткових дій, тому я вивчив дії за замовчуванням і придумав:

[ActionName("builtin")]
public IEnumerable<string> Get()
{
    return this.Repository.GetAll();
}

для першого способу в поєднанні з "подвійним" прив'язкою маршруту:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { action = "builtin", id = RouteParameter.Optional },
    constraints: new { id = @"\d+" });

config.Routes.MapHttpRoute(
    name: "CustomActionApi",
    routeTemplate: "api/{controller}/{action}");

Зауважте, що навіть незважаючи на те, що параметр "дія" у шаблоні першого маршруту, очевидно, ви все одно можете налаштувати дію за замовчуванням, що дозволяє нам відокремити маршрутизацію "звичайних" викликів WebAPI та виклики до додаткової дії.


2

У моєму випадку все було правильно

1) Веб-налаштування налаштовано належним чином 2) Префікс маршруту та атрибути маршруту були належними

І все-таки я отримував помилку. У моєму випадку атрибут "Маршрут" (натисканням клавіші F12) вказував на System.Web.MVc, але не на System.Web.Http, що спричинило проблему.


Ця відповідь мені дуже допомогла!
tvb108108

1

Ви можете додати [Route("api/[controller]/[action]")]до свого класу контролерів.

[Route("api/[controller]/[action]")]
[ApiController]
public class MySuperController : ControllerBase
{
 ...
}

0

Я знаю, що це давнє запитання, але іноді, коли ви використовуєте сервісні ресурси типу AngularJS для підключення до WebAPI, переконайтеся, що ви використовуєте правильний маршрут, інакше ця помилка трапиться.


0

Переконайтесь, що ви НЕ прикрашаєте свої методи контролера для стандартних дій GET | PUT | POST | DELETE за допомогою атрибута [HttpPost / Put / Get / Delete]. Я додав цей атрибут до моєї дії контролера ванільного поста, і це спричинило 404.

Сподіваюсь, це допоможе комусь, оскільки це може бути дуже засмучуючим і привести до успіху.


0

Наприклад => TestController

        [HttpGet]
        public string TestMethod(int arg0)
        {
            return "";
        }

        [HttpGet]
        public string TestMethod2(string arg0)
        {
            return "";
        }

        [HttpGet]
        public string TestMethod3(int arg0,string arg1)
        {
            return "";
        }

Якщо ви можете змінити лише файл WebApiConfig.cs.

 config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/",
                defaults: null
            );

Це воно :)

І результат: введіть тут опис зображення


0

Ви пробували так:

[HttpGet("Summary")]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[HttpGet("FullDetails")]
public HttpResponseMessage FullDetails()
{
    return null;
}

1
Це не буде компілюватися в проектах Core, що HttpGetне є .NET, оскільки в атрибуті немає конструктора, який приймає аргумент рядка.
Hoppeduppeanut
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.