Nicros
Nicros

Reputation: 5183

Web.API MapHttpRoute parameters

I'm having problems with my Web.API routing. I have the following two routes:

config.Routes.MapHttpRoute(
    name: "MethodOne",
    routeTemplate: "api/{controller}/{action}/{id}/{type}",
    defaults: new { id = RouteParameter.Optional, type = RouteParameter.Optional }
);

config.Routes.MapHttpRoute(
    name: "MethodTwo",
    routeTemplate: "api/{controller}/{action}/{directory}/{report}",
    defaults: new { directory = RouteParameter.Optional, report = RouteParameter.Optional }
);

And in my controller these two methods:

[HttpGet]
[ActionName("methodone")]
public string MethodOne(string id, string type)
{
    return string.Empty;
}

[HttpGet]
[ActionName("methodtwo")]
public string MethodTwo(string directory, string report)
{
    return string.Empty;
}

These two seemingly cannot live side by side. If I comment out the MethodOne route in WebApiConfig, the MethodTwo route works. Commenting MethodTwo route allows MethodOne to work. Leaving both uncommented, MethodOne works, but not MethodTwo.

I was hoping to use one route for both of these then it seems they would have to have the same parameter names. Who writes methods with generic parameter names? Bad. I really don't want my methods to have the same parameter names (like p1, p2, p3), so I thought I could create a route just for the new method. But even this doesn't seem to work.

I really miss the WebGet(UriTemplate="") from WCF rest.

I have one controller that has many methods, some with 1, 2, 3 or even more parameters. I can't believe I cant use meaningful parameter names with the MapHttpRoute approach.

I could comment that stuff out entirely and use WebGet() … but before I got there I wanted to see if I'm missing something.

Upvotes: 22

Views: 59704

Answers (3)

Hongmei Ge
Hongmei Ge

Reputation: 276

The reason you are seeing this problem is because your first route will match both requests. The id and type token in the URL will match both requests because when the route is being run, it will try parse the URL and match each segment against your URL.

So your first route will happily match both requests as follows.

~/methodone/1/mytype => action = methodone, id = 1, and type = mytype
~/methodtwo/directory/report => action = methodtwo, id = directory, and type = report

To work around this, you should use route like

config.Routes.MapHttpRoute(
    name: "MethodOne",
    routeTemplate: "api/{controller}/methodone/{id}/{type}",
    defaults: new { id = RouteParameter.Optional, type = RouteParameter.Optional }
);

config.Routes.MapHttpRoute(
    name: "MethodTwo",
    routeTemplate: "api/{controller}/methodtwo/{directory}/{report}",
    defaults: new { directory = RouteParameter.Optional, report = RouteParameter.Optional }
);

Even if you use WebGet, you might need to do something similarly to disambiguous those two methods I believe.

Upvotes: 18

nick66
nick66

Reputation: 226

From http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection

You can also provide constraints, which restrict how a URI segment can match a placeholder:

constraints: new { id = @"\d+" } // Only matches if "id" is one or more digits.

Adding this constraint to "MethodOne" (api/{controller}/{action}/{id}/{type}) would mean that numbers it only matches if {id} is a number otherwise it would match "MethodTwo" ("api/{controller}/{action}/{directory}/{report}").

Upvotes: 6

Youssef Moussaoui
Youssef Moussaoui

Reputation: 12395

You can choose to pass the parameters in the query string like /MethodTwo?directory=a&report=b, but if you'd rather they show up in the path, this looks like a good candidate for attribute-based routing. Filip has a great post about it here:

http://www.strathweb.com/2012/05/attribute-based-routing-in-asp-net-web-api/

Upvotes: 4

Related Questions