Множественный метод 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.асакс.
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.
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.
обязательно, at /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 пользовательскими объектами с разными сигнатурами. Никогда не мог заставить его работать.
- связывание со сложными объектами не выглядит "глубоким", так что это не-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' } )
можно добавить несколько методов Get и Post в одном контроллере веб-API. Здесь маршрут по умолчанию вызывает проблему. Веб-API проверяет соответствие маршрута сверху вниз и, следовательно, ваш маршрут по умолчанию соответствует всем запросам. В соответствии с маршрутом по умолчанию только один метод Get и Post возможен в одном контроллере. Либо поместите следующий код сверху, либо закомментируйте/удалите маршрут по умолчанию
config.Routes.MapHttpRoute("API Default",
"api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
поместите префикс маршрута [RoutePrefix ("api / Profiles")] на уровень контроллера и поместите маршрут в метод действия [Route ("LikeProfile")]. Не нужно ничего менять в глобальном.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()
{
}
}
}
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 as
uri:/api/journal/journalid
uri:/api/journal/journalid/publicationid
так как вы могли видеть мой журнал вроде aggregateroot, хотя я могу определить другой контроллер для публикации исключительно и передать идентификационный номер публикации в моем url, однако это дает гораздо больше смысла. поскольку моя публикация не существовала бы без самого журнала.
Я просто добавил "action=action_name" к url-адресу, и таким образом механизм маршрутизации знает, какое действие я хочу. Я также добавил атрибут ActionName к действиям, но я не уверен, что это необходимо.
Я думаю, что на вопрос уже дан ответ. Я также искал что-то контроллер webApi, который имеет те же signatured 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, и все готово!
надеюсь, что это помогает!