Search code examples
c#.netasp.net-web-apiasp.net-web-api-routing

WebApi 'Multiple actions were found that match the request' error after making call in angular


I feel I have the routes, controller and the call correct, but still cannot figure out why I am getting the 'Multiple actions' error. The controller itself makes call to a service bus, but I cannot get it to hit my breakpoint for GetJob. Any ideas?

Route:

public class WebApiInitializer : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {    
            RouteTable.Routes.MapHttpRoute(
                "webapimethodroutes",
                "api/msi/{controller}/{action}",
                defaults: new {}
                );
        }
    }

My Job Controller:

public class SBJobController : ApiController, IJobsController
    {
        [HttpPost]
        public CreateJobResponse CreateJob(CreateJobRequest request)
        {
            return BusProxy.Call<CreateJobRequest, CreateJobResponse>(request);
        }

        [HttpPost]
        public GetJobResponse GetJob(GetJobRequest request)
        {
            return BusProxy.Call<GetJobRequest, GetJobResponse>(request);
        }
    }

My service call in angular:

function getJobs(pRequest) {
            var request = pRequest || {
                'Ids': [],
                'JobNumbers': [],
                'PageNumber': 1,
                'PageSize': 20,
            };
            return $http.post('/api/msi/SBJob/GetJob', request).error(function (data, status, headers, config) {
                //logError(data);
            });
        }

The request objects:

public class CreateJobRequest : RequestBase
    {
        public JobEntity Job { get; set; }
        public AddressEntity Address { set; get; }
    }

public class GetJobRequest: RequestBase, IPageable
    {
        public int PageNumber { set; get; }
        public int PageSize { set; get; }
        public List<Guid> Ids { set; get; }
        public List<string> JobNumbers { set; get; }
        public Guid ChildOfCustomer { set; get; }
        public Guid ChildOfContact { set; get; }
        public JobTypeEnum JobType { get; set; }
    }

The exact error I am getting:

{
    "Message": "An error has occurred.",
    "ExceptionMessage": "Multiple actions were found that match the request: \r\nCreateJob on type MSI.ServiceBus.ServiceControllers.SBJobController\r\nGetJob on type MSI.ServiceBus.ServiceControllers.SBJobController",
    "ExceptionType": "System.InvalidOperationException",
    "StackTrace": "   at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)\r\n   at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)\r\n   at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}

Solution

  • The routing on web api works on this way

    • To find the controller, Web API adds "Controller" to the value of the {controller} variable.

    • To find the action, Web API looks at the HTTP method, and then looks for an action whose name begins with that HTTP method name. For example, with a GET request, Web API looks for an action that starts with "Get...", such as "GetContact" or "GetAllContacts". This convention applies only to GET, POST, PUT, and DELETE methods. You can enable other HTTP methods by using attributes on your controller. We’ll see an example of that later. Other placeholder variables in the route template, such as {id}, are mapped to action parameters.

    • Instead of using the naming convention for HTTP methods, you can explicitly specify the HTTP method for an action by decorating the action method with the HttpGet, HttpPut, HttpPost, or HttpDelete attribute.

    That's why is matching 2 actions, the name of the method is not used "by default" to match an action. You may have something like this to make it work

    public class WebApiInitializer : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {    
            RouteTable.Routes.MapHttpRoute(
                "CreateJob",
                "api/msi/SBJob/CreateJob",
                defaults: new {Controller = "SBKob", Action = "CreateJob"}
                );
    
            RouteTable.Routes.MapHttpRoute(
                "GetJob",
                "api/msi/SBJob/GetJob",
                defaults: new {Controller = "SBKob", Action = "GetJob"}
                );              
        }
    }