问题
Why can't Web API Core 2 tell these apart?
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values?name=dave
[HttpGet]
public string Get(string name)
{
return $"name is {name}";
}
Here's what happens -
Both http://localhost:65528/api/values
and http://localhost:65528/api/values?name=dave
cause the first Get()
method to execute.
This exact code works fine in Web Api 2.
I know multiple ways of getting around this, but I don't know why it happens.
Can someone explain why this has changed?
回答1:
I don't think you can even compile your code in ASP.NET Core Mvc 2.0
since you have 2 actions mapped to same route [HttGet] api/values
:
AmbiguousActionException: Multiple actions matched.
Remember, ASP.NET Web API
uses the HTTP verb as part of the request to figure which action to call. Although it uses conventional routing (you name your actions Get, Post, Put and Delete, etc) if you don't have route attribute specify, I would highly recommend to always use routing attribute to annotate your controllers and actions.
Api Design time
Now it's up to you to design the route, as a developer. Remember the route is supposed to be a Uri
that can identify a resource / resources.
Use the name as identifier along with the route
[Route("api/[controller]")] public class CustomersController : Controller { // api/customers [HttpGet] public IActionResult Get() { ... } // api/customers/dave [HttpGet("{name:alpha}")] // constraint as a string public IActionResult GetByName(string name) { ... } }
Use the name as filter, against the resource collection
[Route("api/[controller]")] public class CustomersController : Controller { // api/customers // api/customers?name=dave [HttpGet] public IActionResult Get(string name) { ... } }
To confuse you more
api/customers/dave
will still execute GetById
first!
[Route("api/[controller]")]
public class CustomersController : Controller
{
[HttpGet]
public IActionResult Get()
{
...
}
[HttpGet("{name}")]
public IActionResult GetByName(string name)
{
...
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
...
}
}
Both methods GetByName
and GetById
are potential candidates but MVC picks GetById
method first because MVC compares the method/template name {name}
and {id}
through case-insensitive string comparison, and i
comes before n
.
That's when you want to put constraints.
[Route("api/[controller]")]
public class CustomersController : Controller
{
[HttpGet]
public IActionResult Get()
{
...
}
// api/customers/dave
[HttpGet("{name:alpha}")]
public IActionResult GetByName(string name)
{
...
}
// api/customers/3
[HttpGet("{id:int}")]
public IActionResult GetById(int id)
{
...
}
}
You can also specify the Ordering too!
[Route("api/[controller]")]
public class CustomersController : Controller
{
[HttpGet]
public IActionResult Get()
{
...
}
// api/customers/portland
[HttpGet("{city:alpha}", Order = 2)]
public IActionResult GetByCity(string city)
{
...
}
// api/customers/dave
[HttpGet("{name:alpha}", Order = 1)]
public IActionResult GetByName(string name)
{
...
}
// api/customers/3
[HttpGet("{id:int}")]
public IActionResult GetById(int id)
{
...
}
}
Without the Order
, the method GetByCity
will be in favor than GetByName
because character c of {city}
comes before the character n of {name}
. But if you specify the order, MVC will pick the action based on the Order
.
Sigh the post is too long....
回答2:
Because in your case the best match in the route pipeline is the default httpget attribute (the one that get all). The query is a regular string so if you don't ask what you want from the query the best match is still the one that get all.
[HttpGet]
public string Get([FromQuery]string name)
{
return $"name is {name}";
}
The [FromQuery] is pointing to the key "name" in the query string to get the value.
Your should read Routing in asp.net core
来源:https://stackoverflow.com/questions/46674487/web-api-core-2-distinguishing-gets