16

I am trying to create a very basic controller with three get methods Below are the three uri's for them

  1. GET /movies - gets all movies.
  2. GET /movies?name={name}` - finds all movies matching the specified name
  3. Get /Movies/{Id} - Find movie By Id

My code for the controller is as below:

[Route("api/[controller]")]
[ApiController]
public class MoviesController : ControllerBase
{
    private readonly IMoviesService moviesService;

    public MoviesController(IMoviesService moviesService)
    {
        this.moviesService = moviesService;
    }
    
    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var result = await moviesService.GetMoviesAsync();
        return Ok(result);
    }
    
    [HttpGet]
    public async Task<IActionResult> GetByName([FromQuery(Name = "name")] string name)
    {
        var result = await moviesService.GetMoviesByNameAsync(name);
        return Ok(result);
    }

    [HttpGet("{Id}", Name = "GetById")]
    public async Task<IActionResult> GetById(Guid Id)
    {
        var result = await moviesService.GetMovieById(Id);
        return Ok(result);
    }       

}

When I send the request to GetById by api/movies/31234567-89ab-cdef-0123-456789abcdef then it works but for api/movies and api/movies?name=Test I get below error:

The request matched multiple endpoints. Matches: MoviesController.Get and MoviessController.GetByName

Can anyone please suggest me what is the best way to implement such scenario in web API .net core 3.1 considering best practises?

7 Answers 7

32

You'd be better off changing your urls to something like: api/movies/getByName/{name} and api/movies/getById/{id}.

Then:

  1. GET /movies - gets all movies
  2. GET /movies/getByName/{name} - finds all movies matching the specified name
  3. GET /movies/getById/{id} - finds a movie by Id

When adding other actions, you can also add the action name to the route, which will help you avoid The request matched multiple endpoints.

Here is a demo:

[HttpGet]
public async Task<IActionResult> Get()
{
    return Ok();
}

[HttpGet("getByName/{name}")]
public async Task<IActionResult> GetByName(string name)
{
    return Ok();
}

[HttpGet("getById/{id}")]
public async Task<IActionResult> GetById(Guid Id)
{
    return Ok();
} 

Result: enter image description here

Sign up to request clarification or add additional context in comments.

Comments

4

Your Get and GetByName actions have no attribute route provided for them and are both matched to "api/Movies". In order to resolve that you need to add a route template to at least one of those actions, too.

A possible solution is:

[HttpGet]
public async Task<IActionResult> Get()
{
    var result = await moviesService.GetMoviesAsync();
    return Ok(result);
}

[HttpGet]
[Route("{name}")]
public async Task<IActionResult> GetByName([FromRoute] string name)
{
    var result = await moviesService.GetMoviesByNameAsync(name);
    return Ok(result);
}

Note that here the the url for movies with a specific name is no longer /movies?name={name}, but /movies/{name}.

If you absolutely wish to keep the URL /movies?name={name} you can merge the Get and GetByName actions in a single action that accepts a query string parameter name and based on its value execute your logic.

1 Comment

When use /movies/xxxx,it will get The request matched multiple endpoints. Matches: WebAPI.Controllers.MoviesController.GetById (WebAPI) WebAPI.Controllers.MoviesController.GetByName (WebAPI)
4

@Nishant, your is almost good with some minor changes. Below is the working solution. The comments are inline within the code.

[Route("api/[controller]")]
[ApiController]
public class MoviesController : ControllerBase
{
    //GET /movies - gets all movies.
    [HttpGet]
    public async Task<IActionResult> Get()
    {
        return Ok();
    }

    //GET /movies/name={name}` - finds all movies matching the specified name
    [HttpGet("{name}")]  //added the route
    public async Task<IActionResult> GetByName(string name) // modified the input parameter
    {
        return Ok();
    }

    //Get /Movies/{Id} - Find movie By Id
    [HttpGet("{Id:guid}", Name = "GetById")] //added the input type
    public async Task<IActionResult> GetById(Guid Id)
    {
        return Ok();
    }

}

Please let me know if this helps.

Comments

3

You can have multiple methods, keeping in mind that at the beginning of the controller you initialized the way to call the controller methods, like this:

    [Route("api/[controller]")]

.... each action or method must specify [action] and name

 [Route("[action]", Name = "GetPersona")]
    [HttpGet]
    public async Task<IActionResult> GetPersona(int Id)
    {
        bool ok = false;
        string mensaje = "Sin Datos";
        var data = await Task.Run(() => _repositoryPersonas.GetPersona(Id));
        if (data != null)
        {
            mensaje = "ok";
            ok = true;
        }
        return Ok(new { data, ok, mensaje });
    }


    [Route("[action]", Name = "ObtenerPersonas")]
    public async Task<IActionResult> ObtenerPersonas(int Ids)
    {
        bool ok = false;
        string mensaje = "Sin Datos";
        var data = await Task.Run(() => _repositoryPersonas.GetPersona(Ids));
        if (data != null)
        {
            mensaje = "ok";
            ok = true;
        }
        return Ok(new { data, ok, mensaje });
    }

route1https://localhost:44376/api/Personas/GetPersona?Id=1 route2https://localhost:44376/api/Personas/ObtenerPersonas?Id=1

Comments

2

I use this

[HttpGet("[action]")]
public async Task<IActionResult> GetByName([FromQuery(Name = "name")] string name)
{
    var result = await moviesService.GetMoviesByNameAsync(name);
    return Ok(result);
}

1 Comment

This is perfect answer when needs to call by action, without depend only on HTTP verbs
0

Drawing on the accepted answer from 'Yiyi You' I would make slight changes in accordance with the accepted answer (and others) to this question. Using hyphens will make you urls more 'crawlable' by search engines if it is a public api.

[HttpGet]
public async Task<IActionResult> Get()
{
    return Ok();
}

[HttpGet("get-by-name/{name}")]
public async Task<IActionResult> GetByName(string name)
{
    return Ok();
}

[HttpGet("get-by-id/{id}")]
public async Task<IActionResult> GetById(Guid Id)
{
    return Ok();
}

Comments

0

Step 1:- Create a fucntion with api end point and query param enter image description here

Step 2:- Hit the below url in browser (for GET methods) enter image description here

1 Comment

Please read Why should I not upload images of code/data/errors? You can edit your question and replace the images with code blocks. The easiest way to do this is to paste the code as text directly into your answer, then select it and click the code block button.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.