Vários métodos HttpPost no controlador de API da Web


126

Estou começando a usar o projeto MVC4 Web API, tenho um controlador com vários HttpPostmétodos. O controlador é semelhante ao seguinte:

Controlador

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

    [HttpPost]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

Aqui MyRequestTemplaterepresenta a classe de modelo responsável por manipular o Json passando pela solicitação.

Erro:

Quando faço uma solicitação usando o Fiddler para http://localhost:52370/api/VTRouting/TSPRouteou http://localhost:52370/api/VTRouting/Route recebo um erro:

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

Se eu remover um dos métodos acima, ele funciona bem.

Global.asax

Eu tentei modificar a tabela de roteamento padrão global.asax, mas ainda estou recebendo o erro, acho que tenho problemas ao definir rotas no global.asax. Aqui está o que estou fazendo no 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" }
    );
}

Estou fazendo a solicitação no Fiddler usando POST, passando json em RequestBody para MyRequestTemplate.

Respostas:


143

Você pode ter várias ações em um único controlador.

Para isso, você deve fazer as duas coisas a seguir.

  • Primeiro decorar ações com ActionNameatributos 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;
      }
    }
  • Segundo, defina as seguintes rotas no WebApiConfigarquivo.

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

E se eu não quiser definir nenhuma restrição ao tipo de ID? Significado: como também posso aceitar IDs de string?
— Frapontillo

5
@ frapontillo: O ID deve ser um número inteiro, para que seja distinto do nome da rota, caso contrário, o mecanismo de roteamento o tratará como um nome de ação e não como um ID. Se você precisar ter o ID como string, poderá criar uma ação.
— Asif Mushtaq

Eu usaria o roteamento de atributos. Você não precisará usar várias rotas no WebApiConfig dessa maneira. Confira este link: docs.microsoft.com/en-us/aspnet/web-api/overview/…
— Rich

Se eu adicionar assim, ocorrerá um erro ------------ namespace ImageDownloadApplication.Controllers {public class FrontModel {public string skus {get; conjunto; }} [ActionName ("ProductController")] classe pública ProductController: ApiController {// GET: api / NewCotroller public IEnumerable <string> Get () {retorna nova string [] {"value1", "value2"}; }
— Umashankar 16/02

41

Uma solução muito melhor para o seu problema seria usar o Routeque permite especificar a rota no método por anotação:

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

Em que namespace está o Route? Estou usando o MVC4 e o Route não é reconhecido.
— precisa saber é o seguinte


Sim, é assim que deve ser. Obrigado.
— newman

1
por alguma razão, não consigo fazer isso funcionar. isso é exatamente o que eu já estava fazendo.
— Oligofren

2
Como seria o URL do que chamar Routee TSPRoute?
— precisa saber é

27

usar:

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

não é mais uma abordagem RESTful, mas agora você pode chamar suas ações por nome (em vez de permitir que a API da Web determine automaticamente uma para você com base no verbo) assim:

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

Ao contrário da crença popular, não há nada de errado com essa abordagem e ela não está abusando da API da Web. Você ainda pode aproveitar todos os recursos impressionantes da API da Web (delegar manipuladores, negociação de conteúdo, formadores de mídia e assim por diante) - basta abandonar a abordagem RESTful.


1
Obrigado pela resposta, mas ainda está me dando o mesmo erro.
— Habib

Isso não é possível; outra coisa deve ser configurada incorretamente no seu aplicativo. Você pode mostrar toda a configuração da rota? Além disso, como exatamente você está chamando as ações dos controladores?
— 27560 Filipino W

Toda a configuração Route está em global.asax, tenho postar essa parte na minha pergunta, Para fazer pedido, estou usando Fiddler-> Compose-> e selecionando Post como a operação
— Habib

tente remover todas as outras definições de rotas e deixe a que eu publiquei. Então você pode facilmente chamar as duas ações pós localizados dentro de um controlador (o mesmo que abordagem MVC de idade)
— Filip W

1
Filip, estou usando o framework .Net 4.5, com mvc4 ou Visual Studio 2012 RC, qual modelo você está usando para criar seu projeto, seu está funcionando perfeitamente
— Habib

13

Um ponto de extremidade da API da Web (controlador) é um recurso único que aceita verbos get / post / put / delete. É não um controlador normal MVC.

Necessariamente, /api/VTRoutingpode haver apenas um método HttpPost que aceita os parâmetros que você está enviando. O nome da função não importa , desde que você esteja decorando com o material [http]. Eu nunca tentei, no entanto.

Edit: Isso não funciona. Na resolução, parece corresponder ao número de parâmetros, não tentando vincular o modelo ao tipo.

Você pode sobrecarregar as funções para aceitar parâmetros diferentes. Tenho certeza de que você ficaria bem se o declarasse da maneira que faz, mas usasse parâmetros diferentes (incompatíveis) para os métodos. Se os parâmetros forem os mesmos, você não terá sorte, pois o modelo de ligação não saberá qual deles você quis dizer.

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

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

Esta parte funciona

O modelo padrão que eles fornecem quando você cria um novo torna isso bastante explícito, e eu diria que você deve seguir esta convenção:

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

Se você deseja criar uma classe que faça muitas coisas, para o uso do ajax, não há grande motivo para não usar um padrão padrão de controlador / ação. A única diferença real é que suas assinaturas de método não são tão bonitas e é necessário agrupar as coisas Json( returnValue)antes de devolvê-las.

Editar:

A sobrecarga funciona muito bem ao usar o modelo padrão (editado para incluir) ao usar tipos simples. Também testei o contrário, com 2 objetos personalizados com assinaturas diferentes. Nunca consegui fazê-lo funcionar.

  • A vinculação a objetos complexos não parece "profunda", portanto é impossível
  • Você pode contornar isso passando um parâmetro extra, na string de consulta
  • Uma redação melhor do que posso oferecer nas opções disponíveis

Isso funcionou para mim, neste caso, veja onde isso o leva. Exceção apenas para teste.

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

E chamado desta forma o console:

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

Eu tentei alterar os tipos de parâmetro, mas parece que só permite um único método Post no controlador. Obrigado pela sua resposta
— Habib

Eu assumi que ele tentaria vincular o modelo para encontrá-lo, já que você pode sobrecarregar. Porém, ele funciona com diferentes # de parâmetros. Pode não ser tão difícil de reescrevê-lo de fazer isso, porém, mas eles ainda não liberou o código fonte ainda, então eu estou apenas preso olhando feio desmontagem
— Andrew Backer

2
+1 por realmente explicar o motivo pelo qual não está funcionando e a filosofia por trás da API da Web.
— MEMark

Aprecie a quebra ... Eu tinha assumido que era para ser um único POST / PUT / GET por controlador, mas não tinha certeza ... portanto, a razão pela qual procurei. Desde que comecei a desenvolver com o MVC para aplicativos da Web, onde várias ações semelhantes por controlador são a norma ... quase parece um desperdício, para que eu possa entender por que um desenvolvedor gostaria. Existe algo como muitos controladores?
— Anthony Griggs

6

É possível adicionar vários métodos Get e Post no mesmo Web API Controller. Aqui, a rota padrão está causando o problema. A API da Web verifica a rota correspondente da parte superior à inferior e, portanto, a sua rota padrão corresponde a todas as solicitações. Conforme a rota padrão, apenas um método Get e Post é possível em um controlador. Coloque o seguinte código em cima ou Comentar / Excluir rota padrão

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

1

Coloque o prefixo da rota [RoutePrefix ("api / Profiles")] no nível do controlador e coloque uma rota no método de ação [Route ("LikeProfile")]. Não precisa alterar nada no arquivo global.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()
        {
        }
    }
}

0

Eu acho que a pergunta já foi respondida. Eu também estava procurando por algo que fosse um controlador webApi que tivesse os mesmos métodos assinados, mas com nomes diferentes. Eu estava tentando implementar a calculadora como WebApi. A calculadora possui 4 métodos com a mesma assinatura, mas com nomes diferentes.

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

e no arquivo WebApiConfig você já possui

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

Basta definir a autenticação / autorização no IIS e pronto!

Espero que isto ajude!


0

Você pode usar esta abordagem:

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

    [HttpPost("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

-1
public class Journal : ApiController
{
    public MyResult Get(journal id)
    {
        return null;
    }
}

public class Journal : ApiController
{

    public MyResult Get(journal id, publication id)
    {
        return null;
    }
}

Não tenho certeza se a sobrecarga do método get / post viola o conceito de API restfull, mas funciona. Se alguém pudesse esclarecer sobre este assunto. E se eu tiver uma uri como

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

Então, como você pode ver meu diário meio agregado, embora eu possa definir outro controlador para publicação apenas e passar o número de identificação da publicação no meu URL, no entanto, isso dá muito mais sentido. pois minha publicação não existiria sem o próprio periódico.


-1

Acabei de adicionar "action = action_name" ao URL e, dessa forma, o mecanismo de roteamento sabe qual ação eu quero. Também adicionei o atributo ActionName às ações, mas não tenho certeza de que seja necessário.


-1

A melhor e mais simples explicação que eu já vi sobre esse tópico - http://www.binaryintellect.net/articles/9db02aa1-c193-421e-94d0-926e440ed297.aspx

  • Editado -

Eu consegui trabalhar apenas com o Route e não precisava do RoutePrefix.

Por exemplo, no controlador

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomer
([FromBody]CustomerOrder obj)
{
}

e

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomerAndOrder
([FromBody]CustomerOrder obj)
{
}

Em seguida, o nome da função entra no jquery como -

options.url = "/api/customer/PostCustomer";

ou

options.url = "/api/customer/PostCustomerAndOrder";
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.