Multiple actions were found that match the request in Web Api

221,578

Solution 1

Your route map is probably something like this in WebApiConfig.cs:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });

But in order to have multiple actions with the same http method you need to provide webapi with more information via the route like so:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });

Notice that the routeTemplate now includes an action. Lots more info here: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

Update:

Alright, now that I think I understand what you are after here is another take at this:

Perhaps you don't need the action url parameter and should describe the contents that you are after in another way. Since you are saying that the methods are returning data from the same entity then just let the parameters do the describing for you.

For example your two methods could be turned into:

public HttpResponseMessage Get()
{
    return null;
}

public HttpResponseMessage Get(MyVm vm)
{
    return null;
}

What kind of data are you passing in the MyVm object? If you are able to just pass variables through the URI, I would suggest going that route. Otherwise, you'll need to send the object in the body of the request and that isn't very HTTP of you when doing a GET (it works though, just use [FromBody] infront of MyVm).

Hopefully this illustrates that you can have multiple GET methods in a single controller without using the action name or even the [HttpGet] attribute.

Solution 2

Update as of Web API 2.

With this API config in your WebApiConfig.cs file:

public static void Register(HttpConfiguration config)
{
    //// Web API routes
    config.MapHttpAttributeRoutes(); //Don't miss this

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = System.Web.Http.RouteParameter.Optional }
    );
}

You can route our controller like this:

[Route("api/ControllerName/Summary")]
[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[Route("api/ControllerName/FullDetails")]
[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}

Where ControllerName is the name of your controller (without "controller"). This will allow you to get each action with the route detailed above.

For further reading: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Solution 3

In Web API (by default) methods are chosen based on a combination of HTTP method and route values.

MyVm looks like a complex object, read by formatter from the body so you have two identical methods in terms of route data (since neither of them has any parameters from the route) - which makes it impossible for the dispatcher (IHttpActionSelector) to match the appropriate one.

You need to differ them by either querystring or route parameter to resolve ambiguity.

Solution 4

After a lot of searching the web and trying to find the most suitable form for routing map if have found the following

config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id =RouteParameter.Optional }, new { id = @"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");

These mapping applying to both action name mapping and basic http convention (GET,POST,PUT,DELETE)

Solution 5

This is the answer for everyone who knows everything is correct and has checked 50 times.....

Make sure you are not repeatedly looking at RouteConfig.cs.

The file you want to edit is named WebApiConfig.cs

Also, it should probably look exactly like this:

using System.Web.Http;

namespace My.Epic.Website
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
          config.MapHttpAttributeRoutes();

          // api/Country/WithStates
          config.Routes.MapHttpRoute(
            name: "ControllerAndActionOnly",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { },
            constraints: new { action = @"^[a-zA-Z]+([\s][a-zA-Z]+)*$" });

          config.Routes.MapHttpRoute(
            name: "DefaultActionApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
          );
    }
    }
}

I could have saved myself about 3 hours.

Share:
221,578

Related videos on Youtube

chobo2
Author by

chobo2

Updated on July 09, 2021

Comments

  • chobo2
    chobo2 almost 3 years

    I keep getting this error when I try to have 2 "Get" methods

    Multiple actions were found that match the request: webapi

    I been looking around at the other similar questions about this on stack but I don't get it.

    I have 2 different names and using the "HttpGet" attribute

    [HttpGet]
    public HttpResponseMessage Summary(MyVm vm)
    {
        return null;
    }
    
    [HttpGet]
    public HttpResponseMessage FullDetails()
    {
        return null;
    }
    
  • chobo2
    chobo2 over 11 years
    Is there any advantages of doing one way or another? If I do the secondary do I just have to put the Http action on each method? Is that the big draw back?
  • Jed
    Jed over 11 years
    Whether one holds an advantage over the other really depends on your project. If you are building a RESTful api, then you'll want to use the HTTP conventions (GET, POST, PUT, DELETE...). In this case the first block of routing code is the way to go but you will want a different controller for every entity that you expose through the api. Based on your method names, I'm guessing this is not the case so use the more descriptive routing. When your route includes the action you will want to explicitly put the http attribute on each method.
  • chobo2
    chobo2 over 11 years
    but can't you still use the restful conventions even if you use the second method if you just put the right attribute on each method? Well I really can't break it up to a separate entity. I mean it is the same data just less of it. Sure I could just have one get method and always return all the data back and let the client deal with it but if the device is a mobile device you really want to send back as little data as possible otherwise your wasting a person data what can be expensive.
  • Jed
    Jed over 11 years
    @chobo2 Why not just use methods that are named accordingly within the controller? GetSummary(MyVm wm) and GetDetails()
  • chobo2
    chobo2 about 11 years
    Is that not what I was doing in my original post(did not use Get in front but had the attribute on top)
  • Tejas Sharma
    Tejas Sharma over 10 years
    Thanks for your answer, just helped me figure out why route resolution was not working even though both my actions had different names. I'm really confused as to why isn't just the default behavior (i.e. why does't the default route template in webapiconfig.cs inlcude "{action}")!
  • Jed
    Jed over 10 years
    @TejasSharma I'm glad it helped you out! As to why it's not the default behavior: Web API is RESTful by default and expects the action to be based on the HTTP method of the request (GET/PUT/POST/DELETE). Including the action in the url is more of an RPC schema.
  • Bruno
    Bruno about 10 years
    I'd like to add that the routes being updated in the response above are in WebApiConfig.cs and not RouteConfig.cs. It took me a minute to realize this.
  • aruno
    aruno almost 10 years
    @bruno if using areas you can also add 'admin' specific APIs like this in AdminAreaRegistration stackoverflow.com/a/9849011/16940
  • Fredrik Stolpe
    Fredrik Stolpe about 9 years
    For me this worked, but only after changing the order of routes in the route configuration so that the one with action appeared first
  • satish kumar V
    satish kumar V about 9 years
    It might be solution, but not optimum one, anyway +1 from my side :)
  • A.T.
    A.T. over 8 years
    exactly order is important here
  • Leandro De Mello Fagundes
    Leandro De Mello Fagundes over 7 years
    I really liked this solution. My default route still the same and I have an "exception" route for the exceptions
  • nulltron
    nulltron about 6 years
    you can also map the parameters into the url EX: [Route("api/ControllerName/Summary/{vm}")]
  • Hoppeduppeanut
    Hoppeduppeanut about 5 years
    This will not compile in non-.NET Core projects, as the HttpGet attribute does not have a constructor that accepts a string argument.
  • tvb108108
    tvb108108 almost 5 years
    This answer helped me a lot!
  • geedubb
    geedubb about 4 years
    Thanks, you saved me about 3 hours
  • Baku Retsu
    Baku Retsu about 2 years
    Dude. This definitely saved me some hours. Thank you!