Wizaerd
Wizaerd

Reputation: 245

Custom Method calling from WebAPI

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

Answers (1)

andes
andes

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

Related Questions