I have the default Route in Global.asax:
RouteTable.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = System.Web.Http.RouteParameter.Optional } );
I wanted to be able to target a specific function, so I created another route:
RouteTable.Routes.MapHttpRoute( name: "WithActionApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = System.Web.Http.RouteParameter.Optional } );
So, in my controller, I have:
public string Get(int id) { return "object of id id"; } [HttpGet] public IEnumerable<string> ByCategoryId(int id) { return new string[] { "byCategory1", "byCategory2" }; }
Calling .../api/records/bycategoryid/5
will give me what I want. However, calling .../api/records/1
will give me the error
Multiple actions were found that match the request: ...
I understand why that is - the routes just define what URLs are valid, but when it comes to function matching, both Get(int id)
and ByCategoryId(int id)
match api/{controller}/{id}
, which is what confuses the framework.
What do I need to do to get the default API route to work again, and keep the one with {action}
? I thought of creating a different controller named RecordByCategoryIdController
to match the default API route, for which I would request .../api/recordbycategoryid/5
. However, I find that to be a "dirty" (thus unsatisfactory) solution. I've looked for answers on this and no tutorial out there on using a route with {action}
even mentions this issue.
The default route template for Web API is "api/{controller}/{id}". In this template, "api" is a literal path segment, and {controller} and {id} are placeholder variables. When the Web API framework receives an HTTP request, it tries to match the URI against one of the route templates in the routing table.
Web API Controller is similar to ASP.NET MVC controller. It handles incoming HTTP requests and send response back to the caller. Web API controller is a class which can be created under the Controllers folder or any other folder under your project's root folder.
Before I illustrate how an ASP.NET MVC controller can be used as an API or a service, let's recap a few things: Web API controller implements actions that handle GET, POST, PUT and DELETE verbs. Web API framework automatically maps the incoming request to an action based on the incoming requests' HTTP verb.
The route engine uses the same sequence as you add rules into it. Once it gets the first matched rule, it will stop checking other rules and take this to search for controller and action.
So, you should:
Put your specific rules ahead of your general rules(like default), which means use RouteTable.Routes.MapHttpRoute
to map "WithActionApi" first, then "DefaultApi".
Remove the defaults: new { id = System.Web.Http.RouteParameter.Optional }
parameter of your "WithActionApi" rule because once id is optional, url like "/api/{part1}/{part2}" will never goes into "DefaultApi".
Add an named action to your "DefaultApi" to tell the route engine which action to enter. Otherwise once you have more than one actions in your controller, the engine won't know which one to use and throws "Multiple actions were found that match the request: ...". Then to make it matches your Get method, use an ActionNameAttribute.
So your route should like this:
// Map this rule first RouteTable.Routes.MapRoute( "WithActionApi", "api/{controller}/{action}/{id}" ); RouteTable.Routes.MapRoute( "DefaultApi", "api/{controller}/{id}", new { action="DefaultAction", id = System.Web.Http.RouteParameter.Optional } );
And your controller:
[ActionName("DefaultAction")] //Map Action and you can name your method with any text public string Get(int id) { return "object of id id"; } [HttpGet] public IEnumerable<string> ByCategoryId(int id) { return new string[] { "byCategory1", "byCategory2" }; }
You can solve your problem with help of Attribute routing
Controller
[Route("api/category/{categoryId}")] public IEnumerable<Order> GetCategoryId(int categoryId) { ... }
URI in jquery
api/category/1
Route Configuration
using System.Web.Http; namespace WebApplication { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API routes config.MapHttpAttributeRoutes(); // Other Web API configuration not shown. } } }
and your default routing is working as default convention-based routing
Controller
public string Get(int id) { return "object of id id"; }
URI in Jquery
/api/records/1
Route Configuration
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Attribute routing. config.MapHttpAttributeRoutes(); // Convention-based routing. config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
Review article for more information Attribute routing and onvention-based routing here & this
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With