Множественный метод 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, но я все еще получаю ошибку, я думаю, что у меня есть проблема в определении маршрутов в глобальном масштабе.асакс. Вот что я делаю в глобальном масштабе.асакс.

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" }
    );
}

Я делаю запрос в скрипач с помощью POST, передача json в RequestBody для MyRequestTemplate.

9 116

9 ответов:

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

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

  • сначала украсьте действия с как

     [ActionName("route")]`
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
    
  • во-вторых, определите следующие маршруты в .

    // 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}"
    );
    

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

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

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

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

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

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

конечная точка веб-api (контроллер) - это один ресурс, который принимает команды get/post/put/delete. Это не обычный контроллер MVC.

обязательно, в /api/VTRouting может быть только один HttpPost метод, который принимает параметры, которые вы отправляете. Имя функции не важно, пока вы украшаете с [http] вещи. Хотя я никогда не пробовал.

Edit: это не работает. В разрешение, похоже, идет по количеству параметров, не пытаясь привязать модель к типу.

вы можете перегрузить функции, чтобы принять различные параметры. Я уверен, что вы были бы в порядке, если бы вы объявили это так, как вы это делаете, но использовали разные (несовместимые) параметры для методов. Если параметры одинаковы, вам не повезло, поскольку привязка модели не будет знать, какой из них вы имели в виду.

[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:

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

  • связывание со сложными объектами не выглядит "глубоким", так что это 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' } )

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

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

поместите префикс маршрута [RoutePrefix ("api/Profiles")] на уровне контроллера и поместите маршрут в метод действия [Route ("LikeProfile")]. Не нужно ничего менять в глобальном.эйсакс файл

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()
        {
        }
    }
}
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

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

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

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

надеюсь, что это помогает!