How to handle multiple endpoints in ASP.Net Core 3 Web API properly

12,742

Solution 1

You can use route constraint

[HttpGet("{id:int}")]
public async Task<ActionResult<Fighter>> GetFighter(int id) 

[HttpGet("{name}")]
public async Task<ActionResult<IEnumerable<Fighter>>> GetFighter (string name)

Solution 2

I had this problem in Core 3.0. I finally found the solution was to put a route attribute on the action - e.g. [Route("NodeInfo")]. That fixed it

Share:
12,742
merkithuseyin
Author by

merkithuseyin

Hüseyin Merkit [email protected]

Updated on July 28, 2022

Comments

  • merkithuseyin
    merkithuseyin almost 2 years

    I have 2 methods to handle HTTP GET requests, first one for int type input and the other one for string type input.

    //GET : api/Fighters/5
    [HttpGet("{id}")]
    public async Task<ActionResult<Fighter>> GetFighter(int id) 
    {
        var fighter = await _context.Fighters.FindAsync(id);
    
        if (fighter == null) 
        {
            return NotFound();
        }
        return fighter;
    }
    
    // GET: api/Fighters/Alex
    [Route("api/Fighters/{name}")]
    [HttpGet("{name}")]
    public async Task<ActionResult<IEnumerable<Fighter>>> GetFighter (string name) 
    {
        return await _context.Fighters.Where(f => f.Name == name).ToListAsync();
    }
    

    when i send HTTP GET this exception appears (in Postman):

    Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches: 
    
    FighterGameService.Controllers.FightersController.GetFighter (FighterGameService)
    FighterGameService.Controllers.FightersController.GetFighter (FighterGameService)
    FighterGameService.Controllers.FightersController.GetFighter (FighterGameService)
    FighterGameService.Controllers.FightersController.GetFighter (FighterGameService)
       at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)
       at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ProcessFinalCandidates(HttpContext httpContext, CandidateState[] candidateState)
       at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.Select(HttpContext httpContext, CandidateState[] candidateState)
       at Microsoft.AspNetCore.Routing.Matching.DfaMatcher.MatchAsync(HttpContext httpContext)
       at Microsoft.AspNetCore.Routing.Matching.DataSourceDependentMatcher.MatchAsync(HttpContext httpContext)
       at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
       at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
    

    GET api/fighters/1 would cause error obviously since "1" could be either int or string so i solved my problem by combining two methods:

    // GET: api/Fighters/5
    // GET: api/Fighters/Alex
    [HttpGet("{idOrName}")]
    public async Task<ActionResult<IEnumerable<Fighter>>> GetFighter(string idOrName)
    {
        if (Int32.TryParse(idOrName, out int id))
        {
            return await _context.Fighters.Where(f => f.Id == id).ToListAsync();
        }
        else
        {
            return await _context.Fighters.Where(f => f.Name == idOrName).ToListAsync();
        }
    
    }
    

    this works however this doesn't feel right at all. What is the proper way to handle this problem?