Search code examples
c#asp.net-corecontroller.net-8.0

The mere presence of a function causes error when calling an api controller


I am using .NET 8.0 in an ASP.NET Core application.

I have a class as follows:

  /// Controller for diagram retrieval.
  /// </summary>
  [Route("api/Projects")]
  [ApiController]
  [Authorize]
  public class ProjectsEndpoints : ControllerBase
  {
      private IGenesysApiService _apiRequestService;
      private AppSettings _appSettings;

      public ProjectsEndpoints(IGenesysApiService apiClientService, AppSettings appSettings)
      {
          _apiRequestService = apiClientService;
          _appSettings = appSettings;
      }

      public async Task<Project> GetProjects()
      {
          await AddGenesysToken();

          var url = _appSettings.GenesysApiURI + "/Projects";
          var list = await _apiRequestService.GetAsync<Project>(url);

          return list.Body;

      }

      private async Task AddGenesysToken()
      {
          var token = await _apiRequestService.GetAccessTokenForUserAsync();
          _apiRequestService.RequestHeaders.Authorization =
              new AuthenticationHeaderValue("Bearer", token);
      }


  }

If i add a function such as:

    public async Task<Entity> GetEntitiesByProject(string projectId)
    {
        try
        {
            await AddGenesysToken();
            var ret = await _apiRequestService.GetAsync<Entity>($"/entities/{projectId}/f5a2162d-74d3-47a0-9b8b-e51d12db78a0");
            return ret.Body;
        }
        catch (Exception ex)
        {
            return null;
        }

    }

WHICH I DO NOT EVEN CALL, I get the error below when trying to call getprojects().

If i comment out the unused function, it works fine.

Loaded 12.51 MB resources from cache Debugging hotkey: Shift+Alt+D (when application has focus) CSS Hot Reload ignoring https://localhost:44367/_content/Microsoft.FluentUI.AspNetCore.Components/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css because it was inaccessible or had more than 5000 rules. Registered: fail: Sidekick.Client.Shared.Services.ApiClientService[0] Server error. fail: Microsoft.AspNetCore.Components.Web.ErrorBoundary[0] System.NullReferenceException: Object reference not set to an instance of an object. at Sidekick.Client.Features.Projects.Services.ProjectsService.GetProjects() in C:\Source\Sidekick\development\Client\Features\Projects\Services\ProjectsService.cs:line 23 at Sidekick.Client.Features.Diagram.ProjectListComponent.OnInitializedAsync() in C:\Source\Sidekick\development\Client\Features\Diagram\ProjectListComponent.razor:line 51 at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync() at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState) System.NullReferenceException: Object reference not set to an instance of an object. at Sidekick.Client.Features.Projects.Services.ProjectsService.GetProjects() in C:\Source\Sidekick\development\Client\Features\Projects\Services\ProjectsService.cs:line 23 at Sidekick.Client.Features.Diagram.ProjectListComponent.OnInitializedAsync() in C:\Source\Sidekick\development\Client\Features\Diagram\ProjectListComponent.razor:line 51 at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync() at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

I validated that get projects works ONLY if i don't add functions to the class. Very strange. Again, the only change i made was to add the functions that i don't yet call to the class.

I am launching in debugger using IIS Express.


Solution

  • Very strange

    It actually is not. I highly recommend to check out the Routing to controller actions in ASP.NET Core docs. Basically you have defined a single route for your controller via:

    [Route("api/Projects")]
    

    And from the previously linked docs:

    Action definition

    Public methods on a controller, except those with the NonAction attribute, are actions.

    So when you add a second method you end up with 2 actions matching the same route, hence if you try calling it directly you will get something like the following:

    An unhandled exception occurred while processing the request.
    AmbiguousMatchException: The request matched multiple endpoints. Matches:
    
    ProjectsEndpoints.GetProjects (ASPNET8TestApp)
    ProjectsEndpoints.GetEntitiesByProject (ASPNET8TestApp)
    

    You can fix it by providing separate route for it, for example:

    [Route("api/Projects")]
    [ApiController]
    public class ProjectsEndpoints : ControllerBase
    {
        [HttpGet]
        public async Task<Project> GetProjects()
        {
            // ...
        }
    
        [HttpGet("{projectId}/entity")]
        public async Task<Entity> GetEntitiesByProject(string projectId)
        {
            // ...
        }
    }
    

    Which will make GetEntitiesByProject available at path api/Projects/{someProjIdHere}/entity