Reputation: 245
So I'm putting together a proof of concept prototype for a WebAPI app, and all the usual methods work great. But I also wanted to demonstrate being able to call custom methods via WebAPI as well. So I have the following routing in my RouteConfig.cs:
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In the controller I have a simple method:
[HttpGet]
public string Test()
{
return "this is a string";
}
When I attempt to call my method : http://localhost:43225/api/values/test
I get the following error in the brower:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.String Get(Int32)' in 'WebAPI_Listener.Controllers.ValuesController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
</string>
But if I specify an ID at the end of the url, such as http://localhost:43225/api/values/test/1
it works, even though the method itself doesn't take a parameter at all.
So in the routing, if I have the id} set as optional, why doesn't it work when I don't specify an {id}, but does work if I do, even though the method itself isn't expecting an {id}??
Upvotes: 0
Views: 5412
Reputation: 514
You need to declare your API routes in the ~/App_Start/WebAPIConfig.cs Register method. The catchall route in the WebAPIConfig precedes the RouteConfig routes, so your route is never being chosen.
Here is an example for adding a route, similar to yours, to the WebAPIConfig. There is a drawback to this example: you have to explicitly state the HTTP Verbs for a controller that matches this route (i.e. ~/api/values/get/5 instead of ~/localhost:43225/api/values/5).
config.Routes.MapHttpRoute(
name: "CustomAPIActions",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
To avoid this drawback, you could limit the route to a given controller like this:
config.Routes.MapHttpRoute(
name: "ApiNonCrudActionsForMyController",
routeTemplate: "MyController/{action}/{id}",
defaults: new { controller = "MyController", id = RouteParameter.Optional }
);
You could also limit the route to a controller and action, to preserve the standard verbs, but this might become a maintenance issue or point of confusion if you do this a lot.
config.Routes.MapHttpRoute(
name: "ApiNonCrudActionsForMyController",
routeTemplate: "MyController/Test/{id}",
defaults: new { controller = "MyController", action = "Test", id = RouteParameter.Optional }
);
For more details and some other approaches, check this out: http://encosia.com/rest-vs-rpc-in-asp-net-web-api-who-cares-it-does-both/. You could also consider limiting your actions to HTTP Verbs, and adding extra controllers for the custom actions (i.e. ~/localhost:43225/api/TestValues/5).
Upvotes: 5