Найдено несколько действий, соответствующих запросу в Web Api

243

Я получаю эту ошибку, когда пытаюсь использовать 2 метода «Get»

Найдено несколько действий, соответствующих запросу: webapi

Я смотрел вокруг на другие подобные вопросы об этом в стеке, но я не понимаю.

У меня есть 2 разных имени и использую атрибут "HttpGet"

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

[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}
chobo2
источник

Ответы:

486

Ваша карта маршрута, вероятно, примерно такая:

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-actions/routing-in-aspnet-web-api

Обновить:

Хорошо, теперь, когда я думаю, что я понимаю, что вы после здесь, еще один взгляд на это:

Возможно, вам не нужен параметр action url, и вы должны описать содержимое, которое вам нужно, другим способом. Поскольку вы говорите, что методы возвращают данные из одной и той же сущности, просто позвольте параметрам описать вас.

Например, ваши два метода могут быть превращены в:

public HttpResponseMessage Get()
{
    return null;
}

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

Какие данные вы передаете в объект MyVm? Если вы можете просто передавать переменные через URI, я бы предложил пойти по этому пути. В противном случае вам нужно будет отправить объект в теле запроса, и это не очень HTTP с вашей стороны при выполнении GET (хотя это работает, просто используйте [FromBody] перед MyVm).

Надеемся, что это показывает, что вы можете иметь несколько методов GET в одном контроллере, не используя имя действия или даже атрибут [HttpGet].

Джед
источник
Есть ли какие-то преимущества в том или ином случае? Если я делаю второстепенное, я должен просто поместить действие Http в каждый метод? Это большая ничья?
chobo2
3
Имеет ли одно преимущество перед другим, действительно зависит от вашего проекта. Если вы создаете API RESTful, тогда вы захотите использовать соглашения HTTP (GET, POST, PUT, DELETE ...). В этом случае первый блок кода маршрутизации - это путь, но вам понадобится отдельный контроллер для каждого объекта, который вы выставляете через API. Основываясь на именах ваших методов, я предполагаю, что это не тот случай, поэтому используйте более описательную маршрутизацию. Если ваш маршрут включает в себя действие, вы захотите явно указать атрибут http для каждого метода.
Джед
1
@ chobo2 Почему бы просто не использовать методы с соответствующими именами в контроллере? GetSummary (MyVm wm) и GetDetails ()
Jed
1
Спасибо за ваш ответ, просто помог мне понять, почему разрешение маршрута не работает, хотя оба моих действия назывались по-разному. Я действительно запутался, почему не просто поведение по умолчанию (то есть почему шаблон маршрута по умолчанию в webapiconfig.cs не указан "{action}")!
Теджас Шарма
1
@bruno, если вы используете области, вы также можете добавить определенные API-интерфейсы «admin», например, в AdminAreaRegistration stackoverflow.com/a/9849011/16940
Simon_Weaver
67

Обновление с веб-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 - это имя вашего контроллера (без «controller»). Это позволит вам получить каждое действие с маршрутом, описанным выше.

Для дальнейшего чтения: http://www.asp.net/web-api/overview/web-api-routing-and-actions/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 часов.

CarComp
источник
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;

Если пространство имен выглядит следующим образом, то при вызове метода контроллера web 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 / controller, который вызывает проблему, описанную 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}");

Обратите внимание, что, хотя в первом шаблоне маршрута нет параметра «action», очевидно, вы все равно можете настроить действие по умолчанию, позволяющее нам разделять маршрутизацию «обычных» вызовов WebAPI и вызовов на дополнительное действие.

Бьёрн ван Доммелен
источник
2

В моем случае все было правильно

1) Web Config был настроен правильно 2) Префикс маршрута и атрибуты маршрута были правильными

Тем не менее я получаю ошибку. В моем случае атрибут «Маршрут» (нажатием клавиши F12) указывал на System.Web.MVc, но не на System.Web.Http, который вызвал проблему.

Хемант Ватти
источник
Этот ответ мне очень помог!
tvb108108
1

Вы можете добавить [Route("api/[controller]/[action]")]в свой класс контроллера.

[Route("api/[controller]/[action]")]
[ApiController]
public class MySuperController : ControllerBase
{
 ...
}
emert117
источник
0

Я знаю, что это старый вопрос, но иногда, когда вы используете сервисные ресурсы, такие как AngularJS, для подключения к WebAPI, убедитесь, что вы используете правильный маршрут, в противном случае возникает эта ошибка.

Суреш Каушик
источник
0

Убедитесь, что вы НЕ украшаете ваши методы контроллера для действий GET | PUT | POST | DELETE по умолчанию с атрибутом [HttpPost / Put / Get / Delete]. Я добавил этот атрибут к своему действию контроллера Vanilla Post, и он вызвал 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
Это не скомпилируется в не .NET Core проектах, так как у HttpGetатрибута нет конструктора, который принимает строковый аргумент.
Hoppeduppeanut