Foram encontradas várias ações que correspondem à solicitação na API da Web

243

Eu continuo recebendo esse erro quando tento ter 2 métodos "Get"

Foram encontradas várias ações que correspondem à solicitação: webapi

Eu estive examinando outras questões semelhantes sobre isso na pilha, mas não entendi.

Eu tenho 2 nomes diferentes e usando o atributo "HttpGet"

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

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

Respostas:

485

Seu mapa de rotas provavelmente é algo como isto:

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

Mas, para ter várias ações com o mesmo método http, você precisa fornecer à webapi mais informações através da rota, da seguinte maneira:

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

Observe que o routeTemplate agora inclui uma ação. Muito mais informações aqui: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

Atualizar:

Tudo bem, agora que acho que entendi o que você procura, aqui está outra ideia:

Talvez você não precise do parâmetro action url e descreva o conteúdo que procura depois de outra maneira. Como você está dizendo que os métodos estão retornando dados da mesma entidade, deixe os parâmetros fazer a descrição para você.

Por exemplo, seus dois métodos podem ser transformados em:

public HttpResponseMessage Get()
{
    return null;
}

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

Que tipo de dados você está passando no objeto MyVm? Se você conseguir passar variáveis ​​através do URI, sugiro seguir esse caminho. Caso contrário, você precisará enviar o objeto no corpo da solicitação e isso não é muito HTTP da sua parte ao fazer um GET (mas funciona, basta usar [FromBody] na frente do MyVm).

Espero que isso ilustre que você pode ter vários métodos GET em um único controlador sem usar o nome da ação ou mesmo o atributo [HttpGet].

Jed
fonte
Existe alguma vantagem em fazer de uma maneira ou de outra? Se eu fizer o secundário, só preciso colocar a ação Http em cada método? Essa é a grande desvantagem?
precisa saber é o seguinte
3
Se um tem uma vantagem sobre o outro, realmente depende do seu projeto. Se você estiver criando uma API RESTful, convém usar as convenções HTTP (GET, POST, PUT, DELETE ...). Nesse caso, o primeiro bloco de código de roteamento é o caminho a seguir, mas você deseja um controlador diferente para cada entidade que expõe por meio da API. Com base nos nomes dos métodos, acho que esse não é o caso, portanto, use o roteamento mais descritivo. Quando sua rota incluir a ação, você desejará colocar explicitamente o atributo http em cada método.
Jed
1
@ chobo2 Por que não usar apenas métodos nomeados de acordo com o controlador? GetSummary (MyVm wm) e GetDetails () #
Jed
1
Obrigado pela sua resposta, apenas me ajudou a descobrir por que a resolução de rota não estava funcionando, embora ambas as minhas ações tivessem nomes diferentes. Estou realmente confuso sobre o porquê não é apenas o comportamento padrão (ou seja, por que o modelo de rota padrão no webapiconfig.cs não inclui "{action}")!
Tejas Sharma
1
@bruno se utilizando áreas que você também pode adicionar APIs específicas 'admin' como este em AdminAreaRegistration stackoverflow.com/a/9849011/16940
Simon_Weaver
67

Atualize a partir da API da Web 2.

Com esta configuração de API no seu arquivo 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 }
    );
}

Você pode rotear nosso controlador assim:

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

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

Onde ControllerName é o nome do seu controlador (sem "controlador"). Isso permitirá que você obtenha cada ação com a rota detalhada acima.

Para leitura adicional: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Marc Stevenson
fonte
Eu realmente gostei desta solução. Minha rota padrão ainda é a mesma e eu tenho uma rota de "exceção" para as exceções
Leandro De Mello Fagundes
você também pode mapear os parâmetros no URL EX: [Route ("api / ControllerName / Summary / {vm}")]
nulltron
15

Na Web API (por padrão), os métodos são escolhidos com base em uma combinação de método HTTP e valores de rota .

MyVmparece um objeto complexo, lido pelo formatador a partir do corpo para que você tenha dois métodos idênticos em termos de dados da rota (já que nenhum deles possui parâmetros da rota) - o que torna impossível para o expedidor ( IHttpActionSelector) corresponder ao apropriado .

Você precisa diferenciá-los por parâmetro de querystring ou route para resolver a ambiguidade.

Filip W
fonte
14

Depois de muita pesquisa na web e tentando encontrar o formulário mais adequado para o mapa de roteamento, se você encontrou o seguinte

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

Esse mapeamento se aplica ao mapeamento de nomes de ações e à convenção http básica (GET, POST, PUT, DELETE)

Moatasem bakri
fonte
9
Para mim, isso funcionou, mas só depois de mudar a ordem das rotas na configuração percurso, de modo a que tem ação apareceu pela primeira vez
Fredrik Stolpe
exatamente a ordem é importante aqui
AT
5

Sem usar ações, as opções seriam:

  1. mova um dos métodos para um controlador diferente, para que não colidam.

  2. use apenas um método que use o parâmetro e, se for nulo, chame o outro método do seu código.

Joanna Derks
fonte
Pode ser uma solução, mas não ideal, de qualquer maneira +1 do meu lado :)
Satish Kumar V
4

Esta solução funcionou para mim.

Coloque o Route2 primeiro no WebApiConfig. Adicione também HttpGet e HttpPost antes de cada método e inclua o nome do controlador e o nome do método no 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
fonte
4

Esta é a resposta para todos que sabem que tudo está correto e que verificou 50 vezes .....

Verifique se você não está olhando repetidamente RouteConfig.cs.

O arquivo que você deseja editar é nomeado WebApiConfig.cs

Além disso, provavelmente deve ser exatamente assim:

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

Eu poderia ter me salvado cerca de 3 horas.

CarComp
fonte
1
Obrigado, você me salvou cerca de 3 horas
geedubb
3

Descobri que quando tenho dois métodos Get, um sem parâmetro e outro com um tipo complexo como parâmetro, recebo o mesmo erro. Resolvi isso adicionando um parâmetro fictício do tipo int, chamado Id, como meu primeiro parâmetro, seguido pelo meu parâmetro de tipo complexo. Em seguida, adicionei o parâmetro de tipo complexo ao modelo de rota. O seguinte funcionou para mim.

Primeiro obtenha:

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

Segundo get:

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
fonte
3

É possível devido ao uso do controlador MVC em vez do controlador de API da Web. Verifique o espaço para nome no controlador de API da Web, que deve ser o seguinte

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

Se o namespace for o seguinte, será apresentado um erro acima na chamada do método do controlador de API da Web

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

Por favor, verifique se você tem dois métodos com nomes diferentes e os mesmos parâmetros.

Nesse caso, exclua qualquer método e tente.

Ramesh
fonte
2

Eu me deparei com esse problema enquanto tentava aumentar meus controladores WebAPI com ações extras.

Suponha que você teria

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

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

Agora, existem dois métodos que atendem à solicitação de / api / controller que acionam o problema descrito pelo TS.

Eu não queria adicionar parâmetros "fictícios" às minhas ações adicionais, então procurei as ações padrão e criei:

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

para o primeiro método em combinação com a ligação de rota "dupla":

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

Observe que, embora não haja parâmetro "action" no primeiro modelo de rota, aparentemente você ainda pode configurar uma ação padrão que nos permite separar o roteamento das chamadas da WebAPI "normais" e das chamadas para a ação extra.

Bjørn van Dommelen
fonte
2

No meu caso, tudo estava certo

1) Web Config foi configurado corretamente 2) Prefixo da rota e atributos da rota adequados

Ainda assim, eu estava recebendo o erro. No meu caso, o atributo "Rota" (pressionando F12) apontava para System.Web.MVc, mas não para System.Web.Http que causou o problema.

Hemanth Vatti
fonte
Esta resposta me ajudou muito!
Tvb108108 12/07/19
1

Você pode adicionar [Route("api/[controller]/[action]")]à sua classe de controlador.

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

Sei que é uma pergunta antiga, mas às vezes, quando você usa recursos de serviço como o AngularJS para conectar-se à WebAPI, verifique se está usando a rota correta; caso contrário, esse erro ocorre.

Suresh Kaushik
fonte
0

Certifique-se de NÃO decorar seus métodos do Controller para as ações GET | PUT | POST | DELETE padrão com o atributo [HttpPost / Put / Get / Delete]. Eu adicionei esse atributo à minha ação do controlador Post vanilla e causou um 404.

Espero que isso ajude alguém, pois pode ser muito frustrante e interromper o progresso.

Entre
fonte
0

Por exemplo => TestController

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

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

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

Se você puder alterar apenas o arquivo WebApiConfig.cs.

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

É isso aí :)

E resultado: insira a descrição da imagem aqui

Cemre Onur Baş
fonte
0

Você já tentou como:

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

[HttpGet("FullDetails")]
public HttpResponseMessage FullDetails()
{
    return null;
}
Deepak Shaw
fonte
1
Isso não será compilado em projetos HttpGetque não sejam do .NET Core, pois o atributo não possui um construtor que aceite um argumento de string.
Hoppeduppeanut 15/05/19