I'm attempting to setup a Web API routing structure that can handle specific action names at the same time as the generic naming. For example:
GET /Api/Customers/1
PUT /Api/Customers/1
DELETE /Api/Customers/1
GET /Api/Customers/1/Products
GET /Api/Customers/1/MailingAddress
PUT /Api/Customers/1/MailingAddress
I tried modifing my API route to the following, without success:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}/{action}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional });
My actions how return a 500 error "Multiple actions were found that match the request". It appears as though my default "Get" action is confused - it's detecting the names of the other actions and can't decide which one to use.
I thought about specifying a specific default action name on my route defaults (ie/ action = "Get"), but that doesn't work in my case since I also want PUT, POST, and DELETE on the default action type.
Any idea on how to use the default GET/PUT/POST/DELETE naming yet also have custom action names in the same controller?
You can using the ActionName attribute to allow MVC routing to distinguish between the actions.
public class CustomerController : ApiController
{
public HttpResponseMessage Get(int? id)
{
return new HttpResponseMessage()
{
Content = new StringContent("Here is a customer with id: " + id)
};
}
[ActionName("MailingAddress")]
public HttpResponseMessage GetMailingAddress(int? id)
{
return new HttpResponseMessage()
{
Content = new StringContent("Here is a mailing address for a customer with id: " + id)
};
}
[ActionName("Products")]
public HttpResponseMessage GetProducts(int? id)
{
return new HttpResponseMessage()
{
Content = new StringContent("Here are the products for a customer with id: " + id)
};
}
}
Doesn't work, unfortunately. I still get the error "HTTP 500 : Multiple actions were found that match the request."
Could it be a bug? I'm setting ActionName on all of my other "custom named" actions, but the Web API beta is still detecting them as possible "GET" calls - you can see it directly in the response:
"Multiple actions were found that match the request:
Customer Get(System.Guid) on type CustomerController
MailingAddress GetMailingAddress(System.Guid) on type CustomerController
MailingAddress GetBillingAddress(System.Guid) on type CustomerController
MailingAddress GetShippingAddress(System.Guid) on type CustomerController
In my controller I have:
[HttpGet]
public Customer Get(Guid id)
{
return new Customer();
}
[HttpGet, ActionName("MailingAddress")]
public MailingAddress GetMailingAddress(Guid id)
{
return new MailingAddress();
}
[HttpGet, ActionName("BillingAddress")]
public MailingAddress GetBillingAddress(Guid id)
{
return new MailingAddress();
}
"And whoever is removed away from the Fire and admitted to Paradise, he indeed is successful." (The Holy Quran)
Excellent Windows VPS Hosting Imran Baloch MVP, MVB, MCP, MCTS, MCPD
ShadowChaser
Member
69 Points
51 Posts
Default routing combined with custom actions
May 16, 2012 04:21 PM|LINK
I'm attempting to setup a Web API routing structure that can handle specific action names at the same time as the generic naming. For example:
GET /Api/Customers/1
PUT /Api/Customers/1
DELETE /Api/Customers/1
GET /Api/Customers/1/Products
GET /Api/Customers/1/MailingAddress
PUT /Api/Customers/1/MailingAddress
I tried modifing my API route to the following, without success:
routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "{controller}/{id}/{action}", defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional });My actions how return a 500 error "Multiple actions were found that match the request". It appears as though my default "Get" action is confused - it's detecting the names of the other actions and can't decide which one to use.
I thought about specifying a specific default action name on my route defaults (ie/ action = "Get"), but that doesn't work in my case since I also want PUT, POST, and DELETE on the default action type.
Any idea on how to use the default GET/PUT/POST/DELETE naming yet also have custom action names in the same controller?
dannyc78
Member
69 Points
16 Posts
Re: Default routing combined with custom actions
May 16, 2012 05:04 PM|LINK
ShadowChaser,
Try adding api to your routeTemplate.
routes.MapHttpRoute( name: "ActionApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } );ShadowChaser
Member
69 Points
51 Posts
Re: Default routing combined with custom actions
May 16, 2012 05:38 PM|LINK
That won't make any difference. It's just part of the URI - I can make it anything I want without any behavioral differences to the service.
Darrel Mille...
Member
176 Points
56 Posts
Re: Default routing combined with custom actions
May 17, 2012 12:48 PM|LINK
You can using the ActionName attribute to allow MVC routing to distinguish between the actions.
public class CustomerController : ApiController { public HttpResponseMessage Get(int? id) { return new HttpResponseMessage() { Content = new StringContent("Here is a customer with id: " + id) }; } [ActionName("MailingAddress")] public HttpResponseMessage GetMailingAddress(int? id) { return new HttpResponseMessage() { Content = new StringContent("Here is a mailing address for a customer with id: " + id) }; } [ActionName("Products")] public HttpResponseMessage GetProducts(int? id) { return new HttpResponseMessage() { Content = new StringContent("Here are the products for a customer with id: " + id) }; } }ShadowChaser
Member
69 Points
51 Posts
Re: Default routing combined with custom actions
May 18, 2012 05:41 PM|LINK
Doesn't work, unfortunately. I still get the error "HTTP 500 : Multiple actions were found that match the request."
Could it be a bug? I'm setting ActionName on all of my other "custom named" actions, but the Web API beta is still detecting them as possible "GET" calls - you can see it directly in the response:
"Multiple actions were found that match the request:
Customer Get(System.Guid) on type CustomerController
MailingAddress GetMailingAddress(System.Guid) on type CustomerController
MailingAddress GetBillingAddress(System.Guid) on type CustomerController
MailingAddress GetShippingAddress(System.Guid) on type CustomerController
In my controller I have:
[HttpGet] public Customer Get(Guid id) { return new Customer(); } [HttpGet, ActionName("MailingAddress")] public MailingAddress GetMailingAddress(Guid id) { return new MailingAddress(); } [HttpGet, ActionName("BillingAddress")] public MailingAddress GetBillingAddress(Guid id) { return new MailingAddress(); }imran_ku07
All-Star
45815 Points
7698 Posts
MVP
Re: Default routing combined with custom actions
May 26, 2012 03:05 PM|LINK
Check this might help you.
Excellent Windows VPS Hosting
Imran Baloch MVP, MVB, MCP, MCTS, MCPD
Yao Huang Li...
Member
38 Points
9 Posts
Microsoft
Re: Default routing combined with custom actions
Jun 07, 2012 02:28 AM|LINK
Perhaps you can try the following?
1. Insert a route that look like this (using empty action name as the default):
routes.MapHttpRoute( name: "CustomersApi", routeTemplate: "Customers/{id}/{action}", defaults: new { controller="Customers", id = RouteParameter.Optional, action = "" });2. Have the following actions (note that we're using empty ActionName on the first Get(id) action):
[HttpGet, ActionName("")] public Customer Get(Guid id) { return new Customer(); } [HttpGet, ActionName("MailingAddress")] public MailingAddress GetMailingAddress(Guid id) { return new MailingAddress(); } [HttpGet, ActionName("BillingAddress")] public MailingAddress GetBillingAddress(Guid id) { return new MailingAddress(); }strumpflohne...
Member
2 Points
1 Post
Re: Default routing combined with custom actions
Dec 21, 2012 11:08 AM|LINK
Why not create multiple routing configs and let non-matching fall through. I could imagine something like this
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }, constraints: new { id = @"\d+" } ); config.Routes.MapHttpRoute( name: "DefaultApiWithActionNames", routeTemplate: "api/{controller}/{id}/{action}", defaults: new { }, constraints: new { id = @"\d+", action = @"\w*" } );and then have an ApiController method like
[ActionName("MailingAddress")] public object GetMailingAddress(int id) { return "Retrieve the mailing address of the customer with id " + id; }That should work for calls like "/api/customers/4/mailingaddress"