AmbiguousMatchException on controller routes - c#

I am trying to build a basic mini project on .NET Core Web API for basic operations like: GET, POST, PUT, DELETE.
I have the following code in my WeatherForecastController which triggers an AmbiguousMatchException:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Web;
using System.Net.Http;
namespace Webdemo.Controllers
{
[ApiController]
[Route("api/[controller]/[Action]")]
public class WeatherForecastController : ControllerBase
{
static List<string> names = new List<string>()
{
"c","a","b"
};
[HttpGet]
public IEnumerable<string> Get()
{
return names;
}
[HttpGet]
public string Get (int id) {
return names[id];
}
[HttpPost]
public void Post([FromBody]string value)
{
names.Add(value);
}
[HttpPut]
public void Put(int id,[FromBody]string value)
{
names[id] = value;
}
[HttpDelete]
public void Delete(int id)
{
names.RemoveAt(id);
}
}
}

error: AmbiguousMatchException: The request matched multiple endpoints.
[HttpGet]
public IEnumerable<string> Get()
{
return names;
}
[HttpGet]
public string Get (int id) {
return names[id];
}
The issue is related to the above code, try to add a placeholder variable for the unique identifier, change the code as below:
// GET: api/<WeatherForecastController>
[HttpGet]
public IEnumerable<string> Get()
{
return names;
}
// GET: api/<WeatherForecastController>/1
[HttpGet("{id}")] //When this action is invoked, the value of "{id}" in the URL is provided to the method in its id parameter
public string Get (int id) {
return names[id];
}
Edit:
Articles about passing parameters with Asp.net Core API:
Tutorial: Create a web API with ASP.NET Core
Parameter Binding in ASP.NET Web API
Multiple GET And POST Methods In ASP.NET Core Web API

Your Get methods match the same endpoint only with a different set of parameters.
You can work around that by changing the name of one the methods, for example the second Get method could become GetSingle as it seems to fetch a single entry from the names list by its id.
You can do it like:
[HttpGet]
public string GetSingle (int id)
{
return names[id];
}

Related

Parameter Query is not working for API Get Method

If I am calling HttpGet method with Parameter Query , the Get method without parameter is only called . How can I call the second Get method using parameter id=2
http://localhost:10436/api/testapi?id=2
Here is the code
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TestApi1
{
[Route("api/testapi")]
[ApiController]
public class TestApiController : ControllerBase
{
public ActionResult Get()
{
return Ok("Gets Working");
}
[HttpGet("{id:int}")]
public ActionResult Get(int id)
{
return Ok("Gets Working with ID");
}
}
}
All routes were tested using VS 2019 and Postman. They are working properly
if you want to use
http://localhost:10436/api/testapi?id=2
change your action
public ActionResult GetById([FromQuery] int id)
{
return Ok("Gets Working with ID");
}
but if you want to use action you already have , use this url
http://localhost:10436/api/testapi/2
[HttpGet("{id}")]
public async Task<ActionResult> Get (int id)
{
//clarify code
}
And send this request http://localhost:10436/api/testapi?id=2.
Your code would work. your code does not work because of your id uppercase and the lowercase fact I think.
UPDATE
[HttpGet("idName/{id}")]
send this request http://localhost:10436/api/testapi/idName?id=2.
ANOTHER PROCESS
[HttpGet("{id}", Name = "Get")]
public async Task<ActionResult> Get (int id)
{
//clarify code
}
And send this request http://localhost:10436/api/testapi?id=2
ANOTHER PROCESS AGAIN
[HttpGet("{id}", Name = "GetById")]
public async Task<ActionResult> GetById (int id)
{
//clarify code
}
And send this request http://localhost:10436/api/testapi?id=2

.NET Core 3.1/5 Api Querystring parameters do not work

I have noticed something strange with my .Net Core 3.1 and .Net Core 5 Apis. When there are 2 different GET methods to retrieve all records and a single record by id, route based parameters work but querystring parameters do not.
Here is some sample code
using Core5TestApi.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Core5TestApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class DataController : ControllerBase
{
private readonly ILogger<DataController> _logger;
List<Data> allData = new List<Data>{
new Data { Id = 1, Name = "Name 1" },
new Data { Id = 2, Name = "Name 2" }
};
public DataController(ILogger<DataController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<Data> GetAll ()
{
return allData;
}
[HttpGet("{id}")]
[Route("{id}")]
public Data Get(int id)
{
return allData.First(i => i.Id == id);
}
}
}
The following are the urls and the results
Call: http://localhost:51672/api/data
Result:[{"id":1,"name":"Name 1"},{"id":2,"name":"Name 2"}]
Call: http://localhost:51672/api/data/1
Result: {"id":1,"name":"Name 1"}
Call: http://localhost:51672/api/data?id=1
Result: [{"id":1,"name":"Name 1"},{"id":2,"name":"Name 2"}]
The last result ignores the querystring parameter and performs the GetAll.
I feel like I am missing something very basic though I believe I have seen this work before
I have also attempted the [FromQuery] attribute before the parameter in the Get method. This actually breaks the route based parameter path
When you call http://localhost:51672/api/data?id=1 routing ignores ?id=1 since there is no any route for this.
You have to use http://localhost:51672/api/data/1 if you want the record with id=1.
And remove from the last action [Route("{id}")], it should be:
[HttpGet("{id}")]
public Data Get(int id)
{
return allData.First(i => i.Id == id);
}
but if you still want to use http://localhost:51672/api/data?id=1
your action should be:
[HttpGet]
public Data Get([FromQuery] int id)
{
return allData.First(i => i.Id == id);
}
Or you can use one action for both:
//http://localhost:51672/api/data/getAll
[HttpGet("GetAll")]
public IEnumerable<Data> GetAll()
{
return allData.ToArray();
}
[HttpGet("{id0?}")]
[HttpGet]
public Data Get(int id0, [FromQuery] int id)
{
var itemId = id0 == 0 ? id : id0;
if(itemId==0) ...return error
return allData.FirstOrDefault(i => i.Id == itemId);
}

Web API HttpGetAttribute errors

I'm making an API for accessing a SQL Server database. I've written the models and classes that I need, so now need to create a controller for joining everything up.
My controller code at the moment is
using Microsoft.AspNetCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EPOSDatabase_API.DataProvider;
using EPOSDatabase_API.Models;
using System.Web.Http;
namespace EPOSDatabase_API.Controllers
{
[Route("api/[controller]")]
public class CategoryController : ApiController
{
private ICategoryDataProvider categoryDataProvider;
public CategoryController(ICategoryDataProvider categoryDataProvider)
{
this.categoryDataProvider = categoryDataProvider;
}
[HttpGet]
public async Task<IEnumerable<Category>> Get()
{
return await this.categoryDataProvider.GetCategories();
}
[HttpGet("{id}")]
public async Task<Category> Get(int Category_ID)
{
return await this.categoryDataProvider.GetCategory(Category_ID);
}
[HttpPost]
public async Task Post([FromBody]Category category)
{
await this.categoryDataProvider.AddCategory(category);
}
[HttpPut("{id}")]
public async Task Put(int Category_ID, [FromBody]Category category)
{
await this.categoryDataProvider.UpdateCategory(category);
}
[HttpDelete("{id}")]
public async Task Delete(int Category_ID)
{
await this.categoryDataProvider.DeleteCategory(Category_ID);
}
}
}
However for the lines
[HttpGet("{id}")], [HttpPut("{id}")] and [HttpDelete("{id}")] I get an error
'HttpGet/Put/DeleteAttribute' does not contain a constructor that takes 1 argument
Why is this error occurring, am I missing a using reference, perhaps?
[HttpGet]
public async Task<Category> Get(int id)
{
return await this.categoryDataProvider.GetCategory(Category_ID);
}
or
[Route("{id}")]
public async Task<Category> Get(int id)
{
return await this.categoryDataProvider.GetCategory(Category_ID);
}
should both do the trick.
ASP.NET Web API know how to match your method signature to the incoming request.
or if you want to change the default behavior you must keep the variable name as the parameter name in the method signature.
you can read more about Attribute Routing in ASP.NET Web API 2 in here

How to tell apart by Web Api which method to use to return data [duplicate]

This question already has answers here:
How does a method in MVC WebApi map to an http verb?
(2 answers)
Closed 5 years ago.
I am new to asp.net Web Api. I create a simple Web Api App with a ValuesController
What will I get when I make a request :
api/values/5
when there are:
public string Get(int id) { }
public void Delete(int id) { }
methods in the controller.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace my2ndWebApi.Controllers
{
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
}
It depends on the HTTP Verb used when making the request.
A GET request to api/values/5 will match public string Get(int id)
A DELETE request to api/values/5 will match public void Delete(int id).
It is actually indicated in the comments of the sample code provided in the original question.
Reference Routing in ASP.NET Web API

How to add Query parameter to GET request on Web API

I know how to add support for Query parameters on a GET request to the endpoint /resources, for example. That would be:
[HttpGet]
public async Task<IHttpActionResult> GetAll([FromUri] QueryData queryData) {
//... Do some stuff
}
In my example, QueryData would be a class containing all my supported query parameters:
public class QueryParam {
public int Page { get; set; }
public int Size { get; set; }
}
So, then I can call the endpoint: /resources?page=2&size=4, and retrieve those parameters successfully.
But, what about doing the same but on the endpoint: /resources/2??
2 is a segment of the URL and it specifies the id of the resource.
At the moment, I am handling the method as follows:
[HttpGet]
public async Task<IHttpActionResult> Get(int id) {
//Do some stuff...
}
This works fine, but then I tried to modify it as:
[HttpGet]
public async Task<IHttpActionResult> Get(int id, [FromUri] QueryData queryData) {
//Do some stuff...
}
I hope it would work as it did on the other method, but it doesn't. Probably, because of the id, which is not retrieved by query parameters, but instead is part of the URL path.
Is it possible to add query parameters to such an URL, and be able to retrieve both the id and those parameters?
I'm looking forward to be able to call the endpoint: /resources/2?lang=en
One way to achieve what you want would be to read the id manually, like this:
[HttpGet]
public async Task<IHttpActionResult> Get(int id, [FromUri]QueryData queryData) {
id = RouteData.Values["id"];
}
or
[HttpGet]
public async Task<IHttpActionResult> Get([FromUri]QueryData queryData) {
var id = RouteData.Values["id"];
}
If MVC yells about the ambiguity add a route constraint like this:
routes.MapRoute(
"Product",
"Product/{productId}",
new {controller="Product", action="Details"},
new {productId = #"\d+" }
);
Another thing you can try is to add ID as a QueryData property. It MAY populate it!
I was able to get Web API 2.2 properly parsing the route values and query string values with the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using WebAPITest.Models;
namespace WebAPITest.Controllers
{
public class ValuesController : ApiController
{
// GET api/values/5
public async Task<IHttpActionResult> Get(int id, [FromUri] QueryParam queryData)
{
return Ok("value");
}
}
}
Model:
using System;
namespace WebAPITest.Models
{
public class QueryParam
{
public int Page { get; set; }
public int Size { get; set; }
}
}
I adjusted the default ValuesController to use IHttpActionResult instead of string. If you want to differentiate between routes, you can add attribute routes and decorate each action with their respective HTTP method. You shouldn't have to modify your routes directly or perform any extra parameter parsing within your controller actions.

Categories