Se encontraron varias acciones que coinciden con la solicitud en Web Api

243

Sigo recibiendo este error cuando intento tener 2 métodos "Obtener"

Se encontraron varias acciones que coinciden con la solicitud: webapi

He estado mirando las otras preguntas similares sobre esto en la pila, pero no lo entiendo.

Tengo 2 nombres diferentes y uso el atributo "HttpGet"

[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}
chobo2
fuente

Respuestas:

485

Su mapa de ruta es probablemente algo como esto:

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

Pero para tener múltiples acciones con el mismo método http, debe proporcionar a webapi más información a través de la ruta de la siguiente manera:

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

Tenga en cuenta que routeTemplate ahora incluye una acción. Mucha más información aquí: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

Actualizar:

Muy bien, ahora que creo que entiendo lo que estás buscando aquí es otra toma de esto:

Quizás no necesite el parámetro de URL de acción y deba describir los contenidos que busca de otra manera. Como está diciendo que los métodos están devolviendo datos de la misma entidad, simplemente deje que los parámetros hagan la descripción por usted.

Por ejemplo, sus dos métodos podrían convertirse en:

public HttpResponseMessage Get()
{
    return null;
}

public HttpResponseMessage Get(MyVm vm)
{
    return null;
}

¿Qué tipo de datos está pasando en el objeto MyVm? Si solo puede pasar variables a través del URI, sugeriría ir por esa ruta. De lo contrario, deberá enviar el objeto en el cuerpo de la solicitud y eso no es muy HTTP de su parte al hacer un GET (sin embargo, funciona, solo use [FromBody] delante de MyVm).

Con suerte, esto ilustra que puede tener múltiples métodos GET en un solo controlador sin usar el nombre de acción o incluso el atributo [HttpGet].

Jed
fuente
¿Hay alguna ventaja de hacerlo de una forma u otra? Si hago la secundaria, ¿tengo que poner la acción Http en cada método? ¿Es ese el gran inconveniente?
chobo2
3
Si uno tiene una ventaja sobre el otro realmente depende de su proyecto. Si está creando una API RESTful, entonces querrá usar las convenciones HTTP (GET, POST, PUT, DELETE ...). En este caso, el primer bloque de código de enrutamiento es el camino a seguir, pero querrá un controlador diferente para cada entidad que exponga a través de la API. Según los nombres de sus métodos, supongo que este no es el caso, así que use el enrutamiento más descriptivo. Cuando su ruta incluye la acción, querrá poner explícitamente el atributo http en cada método.
Jed
1
@ chobo2 ¿Por qué no solo usar métodos que se nombran en consecuencia dentro del controlador? GetSummary (MyVm wm) y GetDetails ()
Jed
1
Gracias por su respuesta, solo me ayudó a entender por qué la resolución de ruta no funcionaba a pesar de que mis acciones tenían nombres diferentes. Estoy realmente confundido sobre por qué no es solo el comportamiento predeterminado (es decir, ¿por qué la plantilla de ruta predeterminada en webapiconfig.cs no incluye "{acción}")!
Tejas Sharma
1
@bruno si usa áreas, también puede agregar API específicas 'admin' como esta en AdminAreaRegistration stackoverflow.com/a/9849011/16940
Simon_Weaver
67

Actualización a partir de Web API 2.

Con esta configuración de API en su archivo WebApiConfig.cs:

public static void Register(HttpConfiguration config)
{
    //// Web API routes
    config.MapHttpAttributeRoutes(); //Don't miss this

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

Puede enrutar nuestro controlador de esta manera:

[Route("api/ControllerName/Summary")]
[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    rturn null;
}

[Route("api/ControllerName/FullDetails")]
[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}

Donde ControllerName es el nombre de su controlador (sin "controlador"). Esto le permitirá obtener cada acción con la ruta detallada anteriormente.

Para más información: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Marc Stevenson
fuente
Realmente me gustó esta solución. Mi ruta predeterminada sigue siendo la misma y tengo una ruta de "excepción" para las excepciones
Leandro De Mello Fagundes el
también puede asignar los parámetros a la url EX: [Ruta ("api / ControllerName / Summary / {vm}")]
nulltron
15

En la API web (por defecto), los métodos se eligen en función de una combinación de método HTTP y valores de ruta .

MyVmparece un objeto complejo, leído por el formateador del cuerpo para que tenga dos métodos idénticos en términos de datos de ruta (ya que ninguno de ellos tiene ningún parámetro de la ruta), lo que hace imposible que el despachador ( IHttpActionSelector) coincida con el apropiado .

Debe diferenciarlos por cadena de consulta o parámetro de ruta para resolver la ambigüedad.

Filip W
fuente
14

Después de mucho buscar en la web y tratar de encontrar la forma más adecuada para el mapa de enrutamiento si ha encontrado lo siguiente

config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id =RouteParameter.Optional }, new { id = @"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");

Estas asignaciones se aplican tanto a la asignación de nombres de acción como a la convención básica de http (GET, POST, PUT, DELETE)

Moatasem bakri
fuente
99
Para mí esto funcionó, pero solo después de cambiar el orden de las rutas en la configuración de la ruta para que el que tenía acción apareciera primero
Fredrik Stolpe, el
el orden exacto es importante aquí
AL
5

Sin usar acciones las opciones serían:

  1. mueva uno de los métodos a un controlador diferente, para que no choquen.

  2. use solo un método que tome el parámetro, y si es nulo, llame al otro método desde su código.

Joanna Derks
fuente
Puede ser una solución, pero no la óptima, de todos modos +1 de mi lado :)
satish kumar V
4

Esta solución funcionó para mí.

Coloque Route2 primero en WebApiConfig. Agregue también HttpGet y HttpPost antes de cada método e incluya el nombre del controlador y el nombre del método en la url.

WebApiConfig =>

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

Controlador =>

public class ValuesController : ApiController
{

    [HttpPost]
    public string GetCustomer([FromBody] RequestModel req)
    {
        return "Customer";
    }

    [HttpPost]
    public string GetCustomerList([FromBody] RequestModel req)
    {
        return "Customer List";
    }
}

Url =>

http://localhost:7050/api/Values/GetCustomer

http://localhost:7050/api/Values/GetCustomerList
Alan Rezende
fuente
4

Esta es la respuesta para todos los que saben que todo es correcto y lo han verificado 50 veces .....

Asegúrese de no estar mirando repetidamente RouteConfig.cs.

El archivo que desea editar se llama WebApiConfig.cs

Además, probablemente debería verse exactamente así:

using System.Web.Http;

namespace My.Epic.Website
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
          config.MapHttpAttributeRoutes();

          // api/Country/WithStates
          config.Routes.MapHttpRoute(
            name: "ControllerAndActionOnly",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { },
            constraints: new { action = @"^[a-zA-Z]+([\s][a-zA-Z]+)*$" });

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

Podría haberme ahorrado unas 3 horas.

CarComp
fuente
1
Gracias, me salvaste unas 3 horas
geedubb
3

Descubrí que cuando tengo dos métodos Get, uno sin parámetros y otro con un tipo complejo como parámetro, obtengo el mismo error. Resolví esto agregando un parámetro ficticio de tipo int, llamado Id, como mi primer parámetro, seguido de mi parámetro de tipo complejo. Luego agregué el parámetro de tipo complejo a la plantilla de ruta. Lo siguiente funcionó para mí.

Primero obtén:

public IEnumerable<SearchItem> Get()
{
...
}

Segundo obtener:

public IEnumerable<SearchItem> Get(int id, [FromUri] List<string> layers)
{
...
}

WebApiConfig:

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

Es posible debido al uso del controlador MVC en lugar del controlador Web API. Verifique el espacio de nombres en el controlador de API web, debe ser el siguiente

using System.Net;
using System.Net.Http;
using System.Web.Http;

Si el espacio de nombres es el siguiente, se muestra el error anterior en la llamada al método del controlador de API web

using System.Web;
using System.Web.Mvc;
Ujwal Khairnar
fuente
2

Compruebe que tiene dos métodos que tienen el mismo nombre y los mismos parámetros.

Si es así, elimine cualquiera de los métodos e intente.

Ramesh
fuente
2

Me topé con este problema al intentar aumentar mis controladores WebAPI con acciones adicionales.

Asume que tendrías

public IEnumerable<string> Get()
{
    return this.Repository.GetAll();
}

[HttpGet]
public void ReSeed()
{
    // Your custom action here
}

Ahora hay dos métodos que satisfacen la solicitud de / api / controller que desencadena el problema descrito por TS.

No quería agregar parámetros "ficticios" a mis acciones adicionales, así que busqué acciones predeterminadas y se me ocurrió:

[ActionName("builtin")]
public IEnumerable<string> Get()
{
    return this.Repository.GetAll();
}

para el primer método en combinación con el enlace de ruta "dual":

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

config.Routes.MapHttpRoute(
    name: "CustomActionApi",
    routeTemplate: "api/{controller}/{action}");

Tenga en cuenta que aunque no haya un parámetro de "acción" en la primera plantilla de ruta, aparentemente todavía puede configurar una acción predeterminada que nos permita separar el enrutamiento de las llamadas WebAPI "normales" y las llamadas a la acción adicional.

Bjørn van Dommelen
fuente
2

En mi caso todo estaba bien

1) Web Config se configuró correctamente 2) El prefijo de ruta y los atributos de ruta eran correctos

Aún así recibí el error. En mi caso, el atributo "Ruta" (presionando F12) apuntaba a System.Web.MVc pero no a System.Web.Http, que causó el problema.

Hemanth Vatti
fuente
¡Esta respuesta me ayudó mucho!
tvb108108
1

Puede agregar [Route("api/[controller]/[action]")]a su clase de controlador.

[Route("api/[controller]/[action]")]
[ApiController]
public class MySuperController : ControllerBase
{
 ...
}
emert117
fuente
0

Sé que es una pregunta antigua, pero a veces, cuando utiliza recursos de servicio como AngularJS para conectarse a WebAPI, asegúrese de estar utilizando la ruta correcta, de lo contrario, se produce este error.

Suresh Kaushik
fuente
0

Asegúrese de NO decorar sus métodos de Controlador para las acciones predeterminadas GET | PUT | POST | DELETE con el atributo [HttpPost / Put / Get / Delete]. Agregué este atributo a mi acción del controlador Vanilla Post y causó un 404.

Espero que esto ayude a alguien, ya que puede ser muy frustrante y detener el progreso.

Adelante
fuente
0

Por ejemplo => TestController

        [HttpGet]
        public string TestMethod(int arg0)
        {
            return "";
        }

        [HttpGet]
        public string TestMethod2(string arg0)
        {
            return "";
        }

        [HttpGet]
        public string TestMethod3(int arg0,string arg1)
        {
            return "";
        }

Si solo puede cambiar el archivo WebApiConfig.cs.

 config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/",
                defaults: null
            );

Eso es :)

Y resultado: ingrese la descripción de la imagen aquí

Cemre Onur Baş
fuente
0

¿Has probado como:

[HttpGet("Summary")]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[HttpGet("FullDetails")]
public HttpResponseMessage FullDetails()
{
    return null;
}
Deepak Shaw
fuente
1
Esto no se compilará en proyectos que no sean .NET Core, ya que el HttpGetatributo no tiene un constructor que acepte un argumento de cadena.
Hoppeduppeanut