Reputation: 5153
Background
I have a controller
public class WorkOrderController : ApiController
{
// GET: api/WorkOrder
public IEnumerable<WhateverObj> Get()
{
//etc..
}
// GET: api/WorkOrder/123
public WhateverObj Get(string id)
{
//etc..
}
// GET: api/WorkOrder/5/020
public WhateverObj Get(string id, string opID)
{
//etc...
}
}
and the following routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ServicesApi",
routeTemplate: "api/{controller}/{id}/{opID}",
defaults: new { opID = RouteParameter.Optional }
);
This works as expected, I can navigate to the above example URLs.
The Problem
Now i want to create another Controller with only 1 method as follows:
public class FilteredWorkOrderController : ApiController
{
//By WorkCentreID = ABC, XYZ, UVW
public IEnumerable<WhateverObj> Get(string workCentreID)
{
//etc...
}
}
The following URL hits the above method ok.
http://localhost:62793/api/FilteredWorkOrder/?workCentreID=ABC
But the (alternative) form
http://localhost:62793/api/FilteredWorkOrder/ABC
does not work, error message is:
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:62793/api/FilteredWorkOrder/ABC'.","MessageDetail":"No action was found on the controller 'FilteredWorkOrder' that matches the request."}
What route mapping configuration do I need, to get the alternative URI to also work?
I tried
config.Routes.MapHttpRoute(
name: "FilteredApi",
routeTemplate: "api/{controller}/{workCentreID}"
);
but this does NOT work.
I've noticed that in the Filtered controller, if I change my parameter name in Get(string workCenterID)
to Get(string id)
, then both URLs work!
http://localhost:62793/api/FilteredWorkOrder/?id=ABC
http://localhost:62793/api/FilteredWorkOrder/ABC
What is so magical about the parameter name: 'id'?
I want my parameter to be called workCentreID
.
Upvotes: 0
Views: 351
Reputation: 363
I'd suggest you use route attributes. Why?
It's really more readable than patterns.
[Route("{workCentreID}")]
public IEnumerable<WhateverObj> Get(string workCentreID)
{
//etc...
}
Upvotes: 0
Reputation: 5153
The comments / answers pointed me in the right direction to use attribute-based routing. What i needed was
public class FilteredWorkOrderController : ApiController
{
//By WorkCentreID = ABC, XYZ, UVW
[Route("api/WorkOrders/{workCentreID}")]
public IEnumerable<WhateverObj> Get(string workCentreID)
{
//etc...
}
}
and i can make a request with http://localhost:62793/api/WorkOrders/ABC
However, it appears with attribute-based routing, the alternative form does not work, that is, i CANNOT make a request using:
http://localhost:62793/api/WorkOrders/?workCentreID=ABC
Upvotes: 0
Reputation: 930
1) Parameter names in route template should match your action's arguments names. This is a convention. So, when you have registered the default route in config:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ServicesApi",
routeTemplate: "api/{controller}/{id}/{opID}",
defaults: new { opID = RouteParameter.Optional }
);
and try to request http://localhost:62793/api/FilteredWorkOrder/ABC - application can't find an action, because there are no pattern matches.
2) The order, in which routes are registered in your config, matters. If you have more than one potential pattern matches, the engine will choose the first one. After changes, you are describing in second case, the engine looks in routetable and matches again the default route for url http://localhost:62793/api/FilteredWorkOrder/ABC - which doesn't correlate with action signature.
3) For second case - if you will place your custom route registration before default route - your URL should work:
config.Routes.MapHttpRoute(
name: "FilteredApi",
routeTemplate: "api/{controller}/{workCentreID}"
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Besides, take a look at this article, describing routing in WEB API
Upvotes: 2