How to configure routing in ASP.NET CORE 3.0 to use overload [HttpGet] method with [FromQuery] parameters?

China☆狼群 提交于 2020-01-05 08:26:25

问题


Sorry about the title, I'm not good at them, let me know if you know any better title to describe the content of the question.

So I have 2 methods, the first one is to get all the list items, and the second is to get all the list items too, however, there is a query parameter on the second method, that I use to filter, and the second method also returns a different object than the first method. Since I have 2 Http get methods that go to the same route, when I call one of the methods I get:

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

How do I solve this without merging the 2 methods or making use of optional parameters, or changing the path of one method? if possible??

example code:

// GET: api/Resources
[HttpGet]
public async Task<ActionResult<ICollection<Data>>> GetAll()
{
    return Ok(await Service.GetAll());
}

// GET: api/Resources
[HttpGet]
public async Task<ActionResult<Data2>> GetAll([FromQuery]int parameter)
{
    return Ok(await Service.GetAll2(parameter));
}

Inside the configure method I have:

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthentication();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

Edit: Tried as suggested by comments to make use of actions in a configuration like this...

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute("default", "{controller}/{action}/{id?}");
});

It didn't work because the wrong method gets called when I do a get request to, for example, the first getall method: api/resources/getall the method below gets triggered instead causing an error since getall is not an int...

// GET: api/Resources/5
[HttpGet("{id}")]
public async Task<ActionResult<Data>> GetById(int id)
{
    return Ok(await Service.GetById(id));
}

repro example >> https://drive.google.com/open?id=15EB1_kK-c_qyhRS0QvVlKnhuUbFjyN12


Got the actions working now, had to change the attribute routing in the controller...

[Route("api/[controller]/[action]")]
[ApiController]
public class ResourcesController : ControllerBase
{
    // GET: api/Resources/GetAll
    [HttpGet]
    public ActionResult<ICollection<string>> GetAll()
    {
        return Ok(new List<string>() { "foo", "bar" });
    }

    // GET: api/Resources/GetAll2?parameter="bar"
    [HttpGet]
    public ActionResult<string> GetAll2([FromQuery]string parameter)
    {
        return Ok(parameter);
    }

    // GET: api/Resources/GetById/5
    [HttpGet("{id}")]
    public ActionResult<int> GetById(int id)
    {
        return Ok(id);
    }
}

Though since it's not possible to achieve this without having different paths, I'm going with changing the path of only one method instead of using actions in the controller and having to add the action name in the path when calling the methods.


回答1:


As reference I have created a new web api asp.net core 3 project.

Also let's say for example you had the default route registered on your Startup.cs. endpoints.MapDefaultControllerRoute();

Adding

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

At the start of your controller does override your Startup so you do not have to worry about your other controllers.

Also you cannot achieve this with the Id being optional parameter. You would get ambiguity. Since GetAll() and GetAll(int parameter) are precisely the same, since we have declared the parameter as optional. This is why you get the error.

using Microsoft.AspNetCore.Mvc;

namespace WebApiTest.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class ResourceController : ControllerBase
    {

        [HttpGet]
        //api/Resource/GetAll
        public IActionResult GetAll()
        {
            return Content("I got nth");
        }

        //GET: //api/Resource/GetAll/2
        [HttpGet("{parameter}")]
        public IActionResult GetAll(int parameter)
        {
            return Ok($"Parameter {parameter}");
        }

    }
}

Also notice that at the second GetAll() i added in my HttpGet the parameter.

This is just to update the routing Engine that this route will have a parameter since at the generic level at the top of my file i am registering until the action.

For more parameters you can do sth like this. [HttpGet({parameter}/{resourceId})].

Then your route would work similar to this api/Resource/GetAll/2/4.




回答2:


How do I solve this without merging the 2 methods or making use of optional parameters, or changing the path of one method? if possible??

If you'd like to make it work without merging these two actions or specifying action name in route, you can try to use Http[Verb] attributes to make your second action accept a parameter from route data, like below.

// GET: api/Resources
[HttpGet]
public async Task<ActionResult<ICollection<Data>>> GetAll()
{
    return Ok(await Service.GetAll());
}


// GET: api/Resources/1
[HttpGet("{parameter:int}")]
public async Task<ActionResult<Data2>> GetAll([FromRoute]int parameter)
{
    return Ok(await Service.GetAll2(parameter));
}

Besides, in my view, merging these two actions and using an optional parameter would be better. I'd like to know what scenario that requires not using this approach to achieve the requirement.

[HttpGet("{parameter:int?}")]
public async Task<IActionResult> GetAll([FromQuery]int parameter)
{
    if (parameter == 0)
    {
        return Ok(await Service.GetAll());
    }
    else
    {
        return Ok(await Service.GetAll2(parameter));
    }
}


来源:https://stackoverflow.com/questions/59268581/how-to-configure-routing-in-asp-net-core-3-0-to-use-overload-httpget-method-wi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!