Несколько методов HttpPost в контроллере Web API

Я начинаю использовать проект MVC4 Web API, у меня есть контроллер с несколькими методами 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.

Ответ 1

Вы можете иметь несколько действий в одном контроллере.

Для этого вам нужно сделать следующие две вещи.

  • Сначала украсьте действия атрибутом 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 определите следующие маршруты в файле 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}"
    );
    

Ответ 2

использование:

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

это не RESTful-подход, но теперь вы можете вызывать свои действия по имени (а не позволять веб-API автоматически определять его для вас на основе глагола) следующим образом:

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

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

Ответ 3

Гораздо лучшим решением вашей проблемы было бы использование 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;
    }
}

Ответ 4

Конечная точка web-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), прежде чем возвращать их.

Edit:

Перегрузка работает очень хорошо при использовании стандартного шаблона (отредактированного для включения) при использовании простых типов. Я пошел и протестировал другой путь, с двумя пользовательскими объектами с разными сигнатурами. Никогда не мог заставить его работать.

  • Связывание со сложными объектами не выглядит "глубоким", поэтому no-go
  • Вы можете обойти это, передав дополнительный параметр в строке запроса
  • Лучшая запись, чем я могу дать по доступным параметрам

Это сработало для меня в этом случае, посмотри, откуда он. Исключение только для тестирования.

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' } )

Ответ 5

Можно добавить несколько методов Get и Post в один и тот же Web API-контроллер. Здесь default Route вызывает причину. Web API проверяет соответствие маршрута сверху вниз и, следовательно, ваш маршрут по умолчанию для всех запросов. По умолчанию в одном контроллере возможен только один метод Get и Post. Либо поставьте следующий код сверху, либо выведите комментарий/удалить маршрут по умолчанию

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

Ответ 6

Поместите префикс маршрута [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()
        {
        }
    }
}

Ответ 7

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 нарушает концепцию restfull api, но это работает. Если бы кто-нибудь мог просветить этот вопрос. Что делать, если у меня есть uri как

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

так как вы могли видеть мой сортировщик агрегата в моем журнале, хотя я могу определить другой контроллер для публикации исключительно и пройти идентификационный номер публикации в моем url, однако это дает больше смысла. поскольку моя публикация не будет существовать без самого журнала.

Ответ 8

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

Ответ 9

Думаю, на этот вопрос уже был дан ответ. Я также искал что-то для контроллера webApi, у которого есть те же сигнатурные mehtods, но с разными именами. Я пытался реализовать Калькулятор как 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, и все готово!

Надеюсь, это поможет!

Ответ 10

Лучшее и простое объяснение, которое я видел по этой теме - http://www.binaryintellect.net/articles/9db02aa1-c193-421e-94d0-926e440ed297.aspx

  • Отредактировано -

Я работал только с Route и не нуждался в 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";