Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Dois POST dentro da classe

Pode ser que em algum momento eu tenha necessidade de criar dois metodos que recebem post dentro do controllador de fime. por exemplo:

[ApiController] //DEFINED A CONTROLLER
    [Route("[controller]")] //DEFINED ROUTE.. WILL BE /filme THIS CONTRLLER WILL BE CALLED
    public class FilmeController : ControllerBase
    {

        private static List<Filme> filmes = new List<Filme>();

        [HttpPost] //PADRÃO PARA CRIAR DADOS
        public void AdicionaFilme([FromBody] Filme filme) 
        {
            filmes.Add(filme);
            Console.WriteLine(filme.Title);
        }

        [HttpPost] //PADRÃO PARA CRIAR DADOS
        public void teste([FromBody] Filme filme) 
        {
            filmes.Add(filme);
            Console.WriteLine(filme.Title);
        }

    }

quando usar o postman, qual metodo será chamado?

6 respostas

Gostei da Pergunta Rodolfo!

... e fui conferir no meu código... para testar eu fiz o seguinte. Primeiro mantive o código original da aula do "Mapper":

[HttpGet("{id}")]
public IActionResult RecuperaFilmesPorId(int id)
{
    Filme? filme = _context.Filmes.FirstOrDefault(filme => filme.Id == id);

    if (filme != null)
    {
        ReadFilmeDTO filmeDTO = _mapper.Map<ReadFilmeDTO>(filme);

        return Ok(filme);
    }

    return NotFound();
}

Depois eu copiei e criei esta versão "1". Repare que o nome do método é diferente, o conteúdo é diferente, mas a assinatura do método é diferente também:

[HttpGet("{id}")]
public IActionResult RecuperaFilmesPorId1(int id, string TESTE)
{
    Filme? filme = _context.Filmes.FirstOrDefault(filme => filme.Id == id);

    if (filme != null)
    {
        ReadFilmeDTO filmeDTO = new ReadFilmeDTO
        {
            Id = filme.Id,
            Titulo = TESTE,
            Diretor = filme.Diretor,
            Duracao = filme.Duracao,
            Genero = filme.Genero,
            HoraDaConsulta = DateTime.Now
        };

        return Ok(filme);
    }

    return NotFound();
}

O visual Studio compilou de boa, mas nos três sofwares de emulação: Postman, Swagger e o Soap UI apresentara uma tela de erro ao serem evocados...

  • Postman: (ao evocar a chamada com "ID")

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:

Erro Postman

  • Swagger: (logo que foi chamado)

Failed to load API definition.

Errors

Fetch error

response status is 500

https://localhost:7189/swagger/v1/swagger.json

Erro Swagger

  • Soap UI: (ao evocar a chamada com "ID")

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:

Erro Soap UI

Veja se acontece o mesmo no seu código.

[]'s,

Fabio I.

solução!

Boa tarde, tudo em paz? Esses dois métodos irão gerar um erro porque uma requisição não considera os nomes dos métodos e sim suas rotas. Como nesses casos não foram definidas rotas explicitamente, o controlador redirecionará os dois POSTs para a rota padrão (/[controller]). Caso seja necessário definir mais de uma Action com o mesmo método, vai ser preciso explicitar uma rota na anotação do método [HttpPost("SuaRotaAqui")] ou utilizando uma anotação própria [Route("")], logo abaixo do método Http.

Olá Leonardo!

Não entendi, eu fiz desta forma e os dois juntos conflitaram:

[HttpGet("{id}")]
public IActionResult RecuperaFilmesPorId(int id)
{
    Filme? filme = _context.Filmes.FirstOrDefault(filme => filme.Id == id);

    if (filme != null)
    {
        ReadFilmeDTO filmeDTO = _mapper.Map<ReadFilmeDTO>(filme);

        return Ok(filme);
    }

    return NotFound();
}

E depois criei este:

[HttpGet("{id1}")]
public IActionResult RecuperaFilmesPorId1(int id1)
{
    Filme? filme = _context.Filmes.FirstOrDefault(filme => filme.Id == id1);

    if (filme != null)
    {
        ReadFilmeDTO filmeDTO = new ReadFilmeDTO
        {
            Id = filme.Id,
            Titulo = filme.Titulo,
            Diretor = filme.Diretor,
            Duracao = filme.Duracao,
            Genero = filme.Genero,
            HoraDaConsulta = DateTime.Now
        };

        return Ok(filme);
    }

    return NotFound();
}

Separados eles (um deles comentado) funcionam, mas os dois juntos NÃO funcionam.

O que está errado?

[]'s,

Fabio I.

Olá, Fábio! O roteamento funciona da seguinte forma:

  • Caso não seja definido um rotemamento específico para uma Action, esta estará utilizando o roteamento padrão da controller. Vimos isso com o GET e POST que são chamados por /filme, o que os diferencia são os métodos HTTP, portanto não ocorre um conflito.
  • Quando definimos um roteamento específico, a controller faz uma composição do seu valor específico com o roteamento padrão.

O seu exemplo está usando uma rota composta, sendo chamada por fimes/id, sendo id um valor inteiro. Como os dois métodos HTTP são iguais, ocorrerá um conflito porque elas apontam o mesmo endereçamento. Deverá ser criada uma rota diferente em uma das actions para o conflito deixar de existir. Exemplo:

  • [HttpGet("getbyid/{id}")]: rota final - filmes/getbyid/1
  • [HttpGet("newgetbyid/{id}")]: rota final - filmes/newgetbyid/1

Em síntese, as chaves '{}' representam variáveis, o nome que definimos dentro dela não é relevante para o roteamento, somente o seu tipo. Quando você diz [HttpGet("{id}")], o controlador entende que depois de /filmes será informado um valor inteiro, pois o id foi definido como um tipo inteiro nos parâmetros da sua Action. Quando vc diz [HttpGet("{id1}")], o controlador também entende que depois de /filmes será informado um valor inteiro, pois o id1 foi definido como inteiro também. Quando é solicitado filmes/1, o controlador fica confuso e não sabe pra onde direcionar a requisição, há duas possibilidades aqui, então ocorre um conflito e um erro é exibido.

Leonardo,

Agora entendi! Modifiquei somente as rotas, desta forma:

  • [HttpGet("getById/{id}")]

  • [HttpGet("getById1/{id1}")]

... e funcionou perfeitamente. Obrigado!

Valew!

[]'s,

Fabio I.