Múltiple método HttpPost en el controlador Web API

Estoy comenzando a usar el proyecto de la API web MVC4, tengo un controlador con múltiples métodos HttpPost . El controlador se ve así:

Controlador

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

Aquí MyRequestTemplate representa la clase de plantilla responsable de manejar el Json que viene a través de la solicitud.

Error:

Cuando realizo una solicitud usando Fiddler para http://localhost:52370/api/VTRouting/TSPRoute o http://localhost:52370/api/VTRouting/Route obtengo un error:

Se encontraron varias acciones que coinciden con la solicitud

Si elimino uno de los métodos anteriores, funciona bien.

Global.asax

He intentado modificar la tabla de enrutamiento predeterminada en global.asax , pero sigo recibiendo el error, creo que tengo problemas para definir rutas en global.asax. Esto es lo que estoy haciendo en 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" } ); } 

Realizo la solicitud en Fiddler usando POST, pasando json en RequestBody para MyRequestTemplate.

Puede tener múltiples acciones en un solo controlador.

Para eso tienes que hacer las siguientes dos cosas.

  • Primero decorar acciones con el atributo ActionName como

      [ActionName("route")]` public class VTRoutingController : ApiController { [ActionName("route")] public MyResult PostRoute(MyRequestTemplate routingRequestTemplate) { return null; } [ActionName("tspRoute")] public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate) { return null; } } 
  • En segundo lugar, defina las siguientes rutas en el archivo 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}" ); 

utilizar:

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

Ya no es un enfoque RESTful, pero ahora puede llamar sus acciones por su nombre (en lugar de dejar que la API web determine automáticamente una para usted basándose en el verbo) de esta manera:

 [POST] /api/VTRouting/TSPRoute [POST] /api/VTRouting/Route 

Contrariamente a la creencia popular, no hay nada de malo en este enfoque, y no está abusando de la API web. Todavía puede aprovechar todas las increíbles funciones de la API web (delegar controladores, negociación de contenido, formatos de medios, etc.): simplemente abandone el enfoque RESTful.

Una solución mucho mejor a su problema sería usar la Route que le permite especificar la ruta en el método por anotación:

 [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; } } 

Un punto final de la API web (controlador) es un único recurso que acepta verbos get / post / put / delete. No es un controlador MVC normal.

Necesariamente, en /api/VTRouting solo puede haber un método HttpPost que acepte los parámetros que está enviando. El nombre de la función no importa , siempre y cuando esté decorando con las cosas [http]. Aunque nunca lo intenté.

Editar: Esto no funciona. Al resolver, parece ir por el número de parámetros, no tratando de vincular el modelo al tipo.

Puede sobrecargar las funciones para aceptar diferentes parámetros. Estoy bastante seguro de que estarás bien si lo declaraste como lo haces, pero usaste parámetros diferentes (incompatibles) con los métodos. Si los parámetros son los mismos, no tiene suerte, ya que el enlace modelo no sabrá a cuál se refería.

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

Esta parte funciona

La plantilla predeterminada que dan cuando creas una nueva hace que esto sea bastante explícito, y yo diría que debes apegarte a esta convención:

 public class ValuesController : ApiController { // GET is overloaded here. one method takes a param, the other not. // GET api/values public IEnumerable 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) {} } 

Si desea hacer una clase que haga muchas cosas, para el uso de ajax, no hay una razón importante para no usar un patrón de controlador / acción estándar. La única diferencia real es que las firmas de tu método no son tan bonitas, y tienes que ajustar las cosas en Json( returnValue) antes de devolverlas.

Editar:

La sobrecarga funciona bien cuando se usa la plantilla estándar (editada para incluir) cuando se usan tipos simples. He ido y probado a la inversa, con 2 objetos personalizados con diferentes firmas. Nunca podría hacer que funcione.

  • La vinculación con objetos complejos no se ve “profunda”, por lo que es un no-go
  • Puede solucionar esto pasando un parámetro adicional en la cadena de consulta
  • Un mejor informe de lo que puedo dar sobre las opciones disponibles

Esto funcionó para mí en este caso, mira a dónde te lleva. Excepción solo para prueba.

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

Y llamado así desde la consola:

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

Es posible agregar varios métodos de obtención y publicación en el mismo controlador de API web. Aquí la ruta predeterminada está causando el problema. La API web comprueba la coincidencia de ruta de arriba a abajo y, por lo tanto, sus coincidencias de ruta predeterminadas para todas las solicitudes. Según la ruta predeterminada, solo es posible obtener un método de obtención y publicación en un controlador. Coloque el siguiente código en la parte superior o Comente / elimine la ruta predeterminada

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

Coloque Route Prefix [RoutePrefix (“api / Profiles”)] en el nivel del controlador y ponga una ruta en el método de acción [Route (“LikeProfile”)]. No es necesario cambiar nada en el archivo global.asax

 namespace KhandalVipra.Controllers { [RoutePrefix("api/Profiles")] public class ProfilesController : ApiController { // POST: api/Profiles/LikeProfile [Authorize] [HttpPost] [Route("LikeProfile")] [ResponseType(typeof(List))] public async Task 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; } } 

No estoy seguro de si la sobrecarga del método get / post viola el concepto de api restfull, pero funciona. Si alguien podría haber iluminado sobre este asunto. ¿Qué pasa si tengo un uri como

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

por lo que puede que veas mi revista de forma agregada, aunque puedo definir otro controlador para publicación únicamente y pasar el número de identificación de publicación en mi url; sin embargo, esto tiene mucho más sentido. ya que mi publicación no existiría sin la revista misma.

Acabo de agregar “action = action_name” a la url y de esta manera el motor de enrutamiento sabe qué acción quiero. También agregué el atributo ActionName a las acciones, pero no estoy seguro de que sea necesario.

Creo que la pregunta ya ha sido respondida. También estaba buscando algo como un controlador webApi que tenga los mismos mecanismos, pero con diferentes nombres. Estaba tratando de implementar la Calculadora como WebApi. La calculadora tiene 4 métodos con la misma firma pero diferentes nombres.

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

y en el archivo WebApiConfig que ya tienes

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

¡Simplemente configure la autenticación / autorización en IIS y listo!

¡Espero que esto ayude!