I asked this question also on Stack Overflow.
I want to build truly RESTful web service so don't want to leverage RPC-style, so have currently this:
public async Task<IActionResult> GetByParticipant(string participantId, string participantType, string programName)
{
}
public async Task<IActionResult> GetByProgram(string programName)
{
}
I believe that would work in ASP.NET Web API. But I'm getting an exception in Core:
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
TermsController.GetByParticipant (ParticipantTerms.Api)
TermsController.GetByProgram (ParticipantTerms.Api)
Neither of the attributes actually help:
[HttpGet][ActionName][FromQuery]Just disambiguate by putting a route on them.
hi @davidfowl, that's the caveat of my question, if I specify different routes, e.g. /api/terms and /api/terms/all, then I feel it won't be truly/fully/properly RESTful. So wondering how can I have multiple GETs against same resource, i.e. /api/terms.
If that isn't supported out-of-the-box then how can I customize this behavior?
If that isn't supported out-of-the-box then how can I customize this behavior?
Take a look here https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiOverloadingApplicationModelConvention.cs
@abatishchev - I suggest either writing a single action that considers participantId and participantType optional
or
writing an IActionConstraint that selects GetByParticipant when both of those values are present
I installed the shim package:
Install-Package Microsoft.AspNetCore.Mvc.WebApiCompatShim -Version 2.0.0
Then added the convention:
services.AddMvcCore(options =>
{
options.Conventions.Add(container.GetInstance<WebApiOverloadingApplicationModelConvention>());
})
Inherited my controller from ApiController:
public sealed class TermsController : ApiController
{
}
But still getting same exception:
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
Microsoft.UPP.Calc.ParticipantTerms.Api.Controllers.v1.TermsController.GetByParticipant (Microsoft.UPP.Calc.ParticipantTerms.Api)
Microsoft.UPP.Calc.ParticipantTerms.Api.Controllers.v1.TermsController.GetByProgram (Microsoft.UPP.Calc.ParticipantTerms.Api)
Even if two my GETs have completely different parameters.
Please suggest what I might doing wrong. Thanks!
I never meant for you to use the shim, I meant for you to look at the code because it's an example or writing an IActionConstraint. I should have made that more clear.
The short answer is that there's no built-in way using ASP.NET Core MVC to do this, without using add-ons (we don't really consider the Web API compat shim to be a core component).
I'm also not sure that this has anything to do with being RESTful or not. It sounds like you have a "search API" so I would recommend doing something similar to what @rynowak suggested by making the various search parameters optional. I suggest encapsulating them in a class like so:
```c#
public async Task
{
}
public class ParticipantSearchOptions
{
public string ParticipantId { get; set; }
public string ParticipantType { get; set; }
public string ProgramName { get; set; }
}
```
Closing because there are no plans to implement this.
Possible workaround using action constraint.
@davidfowl How do would you achieve an API like this?
class Foo {
Guid? ParentId;
}
GET /api/foos Return all foos
GET /api/foos?parentId={Guid?} all foos where foo.ParentId=={parentId}
[HttpGet]
Get();
[HttpGet]
Get(Guid? parentId)
Won't work because these are seen as the same route. Really needs some distinction between "unbound" and "null"
@mguinness, good find!
I think that the assumption that a non-present value in the URL should bind to a value as default(value) is, erm, not great!
Most helpful comment
@davidfowl How do would you achieve an API like this?
GET /api/foosReturn all foosGET /api/foos?parentId={Guid?}all foos wherefoo.ParentId=={parentId}Won't work because these are seen as the same route. Really needs some distinction between "unbound" and "null"
@mguinness, good find!
I think that the assumption that a non-present value in the URL should bind to a value as default(value) is, erm, not great!