WebAPI 2 intelligently handles the Async suffix on action methods. For example, if I create a default WebAPI project it will route to the correct action regardless of the suffix. I.e.:
http://host/api/controller/action - SUCCEEDS
http://host/api/controller/actionAsync - SUCCEEDS
However, if I create an equivalent controller using MVC 5 the behavior is different:
http://host/controller/actionAsync - SUCCEEDS
http://host/controller/action - FAILS - 404
The fact that it fails with a 404 when the Async suffix isn't present is surprising. Nevertheless, I tried to add a route to handle it, but it still fails:
routes.MapRoute(
name: "DefaultAsync",
url: "{controller}/{action}Async/{id}",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional}
);
Here's the MVC and WebAPI controllers that I used to test (based on a new MVC/WebAPI project with default routes):
public class SampleDto { public string Name; public int Age; }
public class SampleMvcController : Controller
{
public Task<JsonResult> GetJsonAsync()
{
// Illustration Only. This is not a good usage of an Async method,
// in reality I'm calling out to an Async service.
return Task.FromResult(
Json(new SampleDto { Name="Foo", Age = 42}, JsonRequestBehavior.AllowGet));
}
}
public class SampleWebApiController : ApiController
{
public Task<SampleDto> GetJsonAsync()
{
return Task.FromResult(new SampleDto {Name="Bar", Age=24});
}
}
As I'm in the middle of making a bunch of methods async, I'd prefer not to specify an action name. The routing documentation suggests that it can pick up literals which can separate segments, but I haven't had any luck yet.
UPDATE:
The problem is that the Action as retrieved by MVC contains the Async suffix, but no corresponding action (or action name) exists on the controller. The piece that matches the action, MyAction, doesn't identify MyActionAsync as a match.
In retrospect, that's why the route doesn't work. It attempts to identify the action as ending with Async but leave off the Async suffix from the action used in matching, which is not what I wanted to do. It would be useful in the event that I wanted to create only a MyAction method (that was async but didn't follow the Async naming convention) and have it map to the corresponding MyAction method.
<action>Asyncbut not when the URL contains only<action>. My goal is to make MVC see the method<action>Asyncas corresponding to the<action>retrieved from the URL so I don't need to either (a) decorate countless methods with the action name, or (b) violate the Async naming conventions.