Кілька методів HttpPost в контролері Web API


126

Я починаю використовувати проект Web API MVC4, маю контролер з декількома HttpPostметодами. Контролер виглядає наступним чином:

Контролер

public class VTRoutingController : ApiController
{
    [HttpPost]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

Тут MyRequestTemplateпредставлений клас шаблонів, відповідальний за обробку Json, що надходить через запит.

Помилка:

Коли я роблю запит за допомогою Fiddler http://localhost:52370/api/VTRouting/TSPRouteабо http://localhost:52370/api/VTRouting/Route отримую помилку:

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

Якщо я видаляю один із описаних вище способів, він справно працює.

Global.asax

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

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "MyTSPRoute",
        routeTemplate: "api/VTRouting/TSPRoute",
        defaults: new { }
    );

    routes.MapHttpRoute(
        name: "MyRoute",
        routeTemplate: "api/VTRouting/Route",
        defaults: new { action="Route" }
    );
}

Я роблю запит у Fiddler за допомогою POST, передаючи json в RequestBody для MyRequestTemplate.

Відповіді:


143

Ви можете виконати кілька дій в одному контролері.

Для цього вам потрібно зробити наступні дві речі.

  • Спочатку прикрасьте дії з ActionNameподібним атрибутом

     [ActionName("route")]
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
  • По-друге, визначте у WebApiConfigфайлі такі маршрути .

    // Controller Only
    // To handle routes like `/api/VTRouting`
    config.Routes.MapHttpRoute(
        name: "ControllerOnly",
        routeTemplate: "api/{controller}"               
    );
    
    
    // Controller with ID
    // To handle routes like `/api/VTRouting/1`
    config.Routes.MapHttpRoute(
        name: "ControllerAndId",
        routeTemplate: "api/{controller}/{id}",
        defaults: null,
        constraints: new { id = @"^\d+$" } // Only integers 
    );
    
    // Controllers with Actions
    // To handle routes like `/api/VTRouting/route`
    config.Routes.MapHttpRoute(
        name: "ControllerAndAction",
        routeTemplate: "api/{controller}/{action}"
    );

Що робити, якщо я не хочу встановлювати обмеження на тип ідентифікатора? Значення: як я можу також приймати ідентифікатори рядків?
frapontillo

5
@frapontillo: Ідентифікатор повинен бути цілим числом, так що він буде відмежований від назви маршруту, інакше енергія маршрутизації буде вважати це ім'ям дії, а не ідентифікатором. Якщо вам потрібно мати ідентифікатор як рядок, ви можете створити дію.
Асиф Муштак

Я б використав замість маршрутизації атрибутів. Вам не доведеться використовувати декілька маршрутів у WebApiConfig таким чином. Перегляньте це посилання: docs.microsoft.com/en-us/aspnet/web-api/overview/…
Rich

Якщо я додаю так, це дає мені помилку ------------ простір імен ImageDownloadApplication.Controllers {public class FrontModel {public string skus {get; набір; }} [ActionName ("ProductController")] суспільний клас ProductController: ApiController {// GET: api / NewCotroller public IEnumerable <string> Get () {return new string [] {"value1", "value2"}; }
Умашанкар

42

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

[RoutePrefix("api/VTRouting")]
public class VTRoutingController : ApiController
{
    [HttpPost]
    [Route("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    [Route("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

У якому просторі імен знаходиться маршрут? Я використовую MVC4 і маршрут не розпізнається.
eaglei22


Так, це саме шлях. Дякую.
новачок

1
я чомусь не можу змусити це працювати. це саме те, що я вже робив.
олігофрен

2
Як виглядатиме URL-адреса, ніж дзвонити Routeта TSPRoute?
Si8

27

використання:

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

це вже не RESTful підхід, але тепер ви можете називати свої дії по імені (замість того, щоб веб-API автоматично визначав один для вас на основі дієслова):

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

Всупереч поширеній думці, у цьому підході немає нічого поганого, і це не зловживає веб-API. Ви все ще можете скористатись усіма дивовижними можливостями Web API (делегування обробників, узгодження контенту, медіатиформаторів тощо) - ви просто задекоруєте підхід RESTful.


1
Дякую за відповідь, але це все одно дає мені ту саму помилку.
Хабіб

Це неможливо, тоді щось інше має бути неправильно налаштовано у вашому додатку. Чи можете ви показати всю настройку маршруту? Також як саме ви викликаєте дії контролерів?
Filip W

Весь налаштування маршруту знаходиться в global.asax, я розмістив цю частину в своєму запитанні. Для оформлення запиту я використовую Fiddler-> Compose-> і вибрав Post як операцію
Хабіб

спробуйте видалити всі інші визначення маршрутів і просто залиште той, який я опублікував. Тоді ви можете легко викликати обидві дії POST, розташовані в одному контролері (такий же, як і старий підхід MVC)
Filip W

1
Філіп, я використовую .Net Framework 4.5, з mvc4 або Visual studio 2012 RC, який шаблон ви використовуєте для створення свого проекту, ваш 'працює чудово
Хабіб

13

Кінцева точка (контролер) веб-api - це єдиний ресурс, який приймає дієслова get / post / put / delete. Це не звичайний контролер MVC.

Обов'язково, /api/VTRoutingможе бути лише один метод HttpPost, який приймає параметри, які ви надсилаєте. Назва функції не має значення , якщо ви прикрашаєте [http]. Я ніколи не пробував, хоча.

Редагувати: це не працює. Вирішуючи, схоже, йде кількість номерів, не намагаючись прив’язати модель до типу.

Ви можете перевантажувати функції, щоб приймати різні параметри. Я впевнений, що ви були б у порядку, якби ви заявили це так, як ви робите, але використовували різні (несумісні) параметри для методів. Якщо парами однакові, вам не пощастить, оскільки прив'язка моделі не дізнається, яку саме ви мали на увазі.

[HttpPost]
public MyResult Route(MyRequestTemplate routingRequestTemplate) {...}

[HttpPost]
public MyResult TSPRoute(MyOtherTemplate routingRequestTemplate) {...}

Ця частина працює

Шаблон за замовчуванням, який вони дають при створенні нового, робить це досить явним, і я б сказав, що ви повинні дотримуватися цієї конвенції:

public class ValuesController : ApiController
{
    // GET is overloaded here.  one method takes a param, the other not.
    // GET api/values  
    public IEnumerable<string> Get() { .. return new string[] ... }
    // GET api/values/5
    public string Get(int id) { return "hi there"; }

    // POST api/values (OVERLOADED)
    public void Post(string value) { ... }
    public void Post(string value, string anotherValue) { ... }
    // PUT api/values/5
    public void Put(int id, string value) {}
    // DELETE api/values/5
    public void Delete(int id) {}
}

Якщо ви хочете зробити один клас, який робить багато речей, для використання ajax, немає великих причин не використовувати стандартний шаблон контролера / дії. Єдина реальна відмінність полягає в тому, що підписи методу не такі гарні, і вам доведеться обгортати речі, Json( returnValue)перш ніж їх повернути.

Редагувати:

Перевантаження дуже добре працює при використанні стандартного шаблону (відредагований для включення) при використанні простих типів. Я теж пішов і протестував інший шлях, з двома власними об’єктами з різними підписами. Ніколи не міг змусити його працювати.

  • Зв'язування зі складними предметами не виглядає "глибоким", так що це не потрібно
  • Ви можете його обійти, передавши додатковий параметр на рядку запиту
  • Краще записати, ніж я можу дати на доступні варіанти

Це працювало для мене в цьому випадку, дивіться, куди вас дістає. Виняток лише для тестування.

public class NerdyController : ApiController
{
    public void Post(string type, Obj o) { 
        throw new Exception("Type=" + type + ", o.Name=" + o.Name ); 
    }
}

public class Obj {
    public string Name { get; set; }
    public string Age { get; set; }
}

І називається така форма консолі:

$.post("/api/Nerdy?type=white", { 'Name':'Slim', 'Age':'21' } )

Я спробував змінити типи параметрів, але, схоже, це дозволяє лише один метод Post в контролері. Дякуємо за вашу відповідь
Хабіб

Я припускав, що він спробує прив'язування моделі, щоб знайти його, оскільки ви можете перевантажити. Однак це працює з різними параметрами #. Це може бути не так важко переписати, щоб це зробити, хоча вони ще не випустили вихідний код, тому я просто застряг, дивлячись на потворні розбирання
Ендрю Бекер

2
+1 за те, що насправді пояснює причину, що вона не працює, та філософію веб-api.
ПАМ'ЯТ

Вдячний за розбиття ... Я припускав, що це повинен бути один POST / PUT / GET на контролер, але я не був впевнений ... таким чином, причиною, чому я подивився на це. Оскільки я почав розробляти MVC для веб-додатків, де кілька подібних дій на контролер є нормою ... це майже здається марною, тому я можу зрозуміти, чому розробник хотів би цього зробити. Чи існує така річ, як занадто багато контролерів?
Ентоні Гріггс

6

Додавання декількох методів отримання та опублікування можливо в один і той же контролер Web API. Тут Маршрут за замовчуванням викликає проблему. Веб-API перевіряє відповідність маршруту від верху до низу і, отже, ваші відповідні маршрути за замовчуванням для всіх запитів. Відповідно до маршруту за замовчуванням, в одному контролері можливий лише один метод отримання та опублікування. Поставте наступний код вгорі або коментуйте / видаліть маршрут за замовчуванням

    config.Routes.MapHttpRoute("API Default", 
                               "api/{controller}/{action}/{id}",
                               new { id = RouteParameter.Optional });

1

Помістіть префікс маршруту [RoutePrefix ("api / Profiles")] на рівень контролера і поставте маршрут методом дії [Route ("LikeProfile")]. Не потрібно нічого змінювати у файлі global.asax

namespace KhandalVipra.Controllers
{
    [RoutePrefix("api/Profiles")]
    public class ProfilesController : ApiController
    {
        // POST: api/Profiles/LikeProfile
        [Authorize]
        [HttpPost]
        [Route("LikeProfile")]
        [ResponseType(typeof(List<Like>))]
        public async Task<IHttpActionResult> LikeProfile()
        {
        }
    }
}

0

Я думаю, що на питання вже відповіли. Я також шукав щось контролер webApi, який має такі ж сигнали меходів, але різні назви. Я намагався реалізувати Калькулятор як WebApi. Калькулятор має 4 методи з однаковою підписом, але різними іменами.

public class CalculatorController : ApiController
{
    [HttpGet]
    [ActionName("Add")]
    public string Add(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Add = {0}", num1 + num2);
    }

    [HttpGet]
    [ActionName("Sub")]
    public string Sub(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Subtract result = {0}", num1 - num2);
    }

    [HttpGet]
    [ActionName("Mul")]
    public string Mul(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Multiplication result = {0}", num1 * num2);
    }

    [HttpGet]
    [ActionName("Div")]
    public string Div(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Division result = {0}", num1 / num2);
    }
}

і у файлі WebApiConfig, який ви вже маєте

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

Просто встановіть автентифікацію / авторизацію на IIS, і ви закінчите!

Сподіваюся, це допомагає!


0

Ви можете використовувати такий підхід:

public class VTRoutingController : ApiController
{
    [HttpPost("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

-1
public class Journal : ApiController
{
    public MyResult Get(journal id)
    {
        return null;
    }
}

public class Journal : ApiController
{

    public MyResult Get(journal id, publication id)
    {
        return null;
    }
}

Я не впевнений, чи перегрузка методу get / post порушує концепцію спокійних api, але це працює. Якби хто міг би просвітити цю справу. Що робити, якщо у мене є урі

uri:/api/journal/journalid
uri:/api/journal/journalid/publicationid

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


-1

Я щойно додав до URL-адреси "action = action_name", і таким чином двигун маршрутизації знає, яку дію я хочу. Я також додав атрибут ActionName до дій, але не впевнений, що це потрібно.


-1

Найкраще і найпростіше пояснення, яке я бачив на цю тему - http://www.binaryintellect.net/articles/9db02aa1-c193-421e-94d0-926e440ed297.aspx

  • Відредаговано -

Я отримав це, працюючи лише з маршрутом, і RoutePrefix не потребував.

Наприклад, у контролері

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomer
([FromBody]CustomerOrder obj)
{
}

і

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomerAndOrder
([FromBody]CustomerOrder obj)
{
}

Потім ім'я функції переходить у jquery як -

options.url = "/api/customer/PostCustomer";

або

options.url = "/api/customer/PostCustomerAndOrder";
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.