5
votes

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()"
}
3
what do theGetJobRequest and CreateJobRequest objects look like? I think that maybe they both match your request objectSam I am says Reinstate Monica
and why is GetJob HttpPost?Sam I am says Reinstate Monica
No they are different. It has to be a Post because the service bus we are using doesnt accept parameters in a GET requestsanghas26
we're still missing some information. You can't get a 'Multiple Actions...' error with only one route and one action. Posting the code for just the action you want to hit doesn't help us to see why it's hitting one of the other ones that you didn't post.Claies
There are two actions i posted right? the GetJob and CreateJob. As for the route, yes thats the only one I have.sanghas26

3 Answers

14
votes

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"}
            );              
    }
}
4
votes

why not be RESTful with your controller instead of having two Posts, when its really a Get?

    public CreateJobResponse Post(CreateJobRequest request)
    {
        return BusProxy.Call<CreateJobRequest, CreateJobResponse>(request);
    }

    public GetJobResponse Get(int id)
    {
        return BusProxy.Call<GetJobRequest, GetJobResponse>(id);
    }
0
votes

I had a similar issue and it wasn't a routing definition problem - the problem was the signature of the calls appeared to be the same and thus the multiple-action issue. The rest of the error message was actually very useful, as it specifically identifies what the multiple actions are that it can't resolve. In my case my post was returning a complex type:

// POST: api/Transfer
[ResponseType(typeof(Transfer))]
public IHttpActionResult PostTransfer(Transfer transfer)

And it was getting confused with a method in the same file with a similar signature:

public TransferResponse doTransfer(Transfer transfer)

I was a bit shocked that the routing was matching this routine which doesn't start with 'Post'. I tried changing routes. I tried changing the name of the method, but none of that made any difference. What did work was to change the call signature to be slightly different - in this case a added a junk bool value:

public TransferResponse doTransfer(Transfer transfer, bool foo)

Then the routing was happy and worked again.