Search code examples
c#.net-coreodataasp.net-core-webapi

Controller action HttpGet executes wrong action


I have created a new Web API project from the Visual Studio Templates and then I have followed the following tutorial for adding OData to this project. https://devblogs.microsoft.com/odata/supercharging-asp-net-core-api-with-odata/

Calling https://localhost:xxx/api/Assets and https://localhost:xxx/api/Assets/1

return all Assets, while the latter should return only 1 asset (where id = 1)

My code:

public class AssetsController : ControllerBase
{
    private IAssetService _service;
    private IMapper _mapper;

    public AssetsController (IAssetService _service, IMapper mapper)
    {
        this._service = _service;
        this._mapper = mapper;
    }

    [HttpGet]
    [EnableQuery()]
    public ActionResult<IEnumerable<Asset>> Get()
    {
        return this._service.GetAllAssets().ToList();
    }


    [HttpGet("{id}")]
    [EnableQuery()]
    public Asset Get(int id)
    {
        return _service.GetById(id);
    }
}

I have debugged to verify that the Get(int id) function is never called.

I have tried defining my route explicitly like this:

[HttpGet]
[Route("GetById/{id}")]
[EnableQuery()]
public Asset Get(int id)
{
    return _service.GetById(id);
}

EDIT

Routing defined in startup:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            /* snip */

            app.UseMvc(routeBuilder =>
            {
                routeBuilder.MapRoute(
                    name: "default",
                    template: "{controller}/{action=Index}/{id?}");

                routeBuilder.Select().Filter().OrderBy().Expand().Count().MaxTop(10);
                routeBuilder.MapODataServiceRoute("api", "api", GetEdmModel());
            });

        }

This makes no difference.

Any ideas?


Solution

  • There're two approaches to solve this question.

    Approach 1 : rename the id parameter to key

    According to the OData v4 Web API docs :

    Here are some rules for the method signatures:

    • If the path contains a key, the action should have a parameter named key.
    • If the path contains a key into a navigation property, the action should have a > parameter named relatedKey.
    • POST and PUT requests take a parameter of the entity type.
    • PATCH requests take a parameter of type Delta, where T is the entity type.

    We should have a parameter named key:

    [HttpGet("{id}")]  // actually, this line doesn't matter if you're using OData, but it's better to rename it to `key` too
    [EnableQuery()]
    public IActionResult Get(int key)
    {
        ....
    }
    

    Approach 2: rename the Get method to GetAsset

    According to OData v4 Web API docs:

    When Web API gets an OData request, it maps the request to a controller name and an action name. The mapping is based on the HTTP method and the URI. For example, GET /odata/Products(1) maps to ProductsController.GetProduct.

    We could also rename the action method to GetAsset as below:

    [HttpGet("{id}")]
    [EnableQuery()]
    public IActionResult GetAsset(int id)
    {
        ... 
    }