WebAPI 2智能处理操作方法的Async后缀。例如,如果我创建一个默认的WebAPI项目,它将路由到正确的操作,而不管后缀如何。即:
http://host/api/controller/action - SUCCEEDS
http://host/api/controller/actionAsync - SUCCEEDS
但是,如果我使用MVC 5创建一个等效的控制器,行为是不同的:
http://host/controller/actionAsync - SUCCEEDS
http://host/controller/action - FAILS - 404
当Async后缀不存在时,404失败的事实令人惊讶。然而,我试图添加一个路由来处理它,但它仍然失败:
routes.MapRoute(
name: "DefaultAsync",
url: "{controller}/{action}Async/{id}",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional}
);
这是我用来测试的MVC和WebAPI控制器(基于具有默认路由的新MVC / WebAPI项目):
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});
}
}
由于我正在制作一堆异步方法,所以我不想指定一个动作名称。 routing documentation表明它可以获取可以分隔段的文字,但我还没有运气。
更新
问题是由MVC检索的Action包含Async后缀,但控制器上不存在相应的操作(或操作名称)。与操作MyAction
匹配的文章不会将MyActionAsync
标识为匹配。
回想起来,这就是为什么路线不起作用的原因。它尝试将操作标识为以Async结尾,但是从匹配中使用的操作中取消Async后缀,这不是我想要做的。如果我只想创建一个MyAction
方法(异步但不遵循异步命名约定)并将其映射到相应的MyAction
方法,那将非常有用。
答案 0 :(得分:1)
最初,我非常失望AsyncController类型完全符合我们正在寻找的开箱即用的东西。但显然它是老套的,并且仍然只是“与MVC3的向后兼容性。”
所以我最终做的是制作一个自定义的AsyncControllerActionInvoker并将其分配给自定义控制器的ActionInvoker。 CustomAsyncControllerActionInvoker会覆盖BeginInvokeAction方法,以查看是否存在以“Async”结尾的操作方法以执行相应的操作(例如,您在“Index”中传入,它会查找“IndexAsync”)。如果是,请调用那个,否则,继续按原样继续。
public class HomeController : CustomController
{
public async Task<ActionResult> IndexAsync()
{
ViewBag.Header = "I am a page header."
var model = new List<int> {1, 2, 3, 4, 5};
await Task.Run(() => "I'm a task");
return View(model);
}
public ActionResult About()
{
return View();
}
}
public class CustomController : Controller
{
public CustomController()
{
ActionInvoker = new CustomAsyncControllerActionInvoker();
}
}
public class CustomAsyncControllerActionInvoker : AsyncControllerActionInvoker
{
public override IAsyncResult BeginInvokeAction(ControllerContext controllerContext, string actionName, AsyncCallback callback, object state)
{
var asyncAction = FindAction(controllerContext, GetControllerDescriptor(controllerContext), $"{actionName}Async");
return asyncAction != null
? base.BeginInvokeAction(controllerContext, $"{actionName}Async", callback, state)
: base.BeginInvokeAction(controllerContext, actionName, callback, state);
}
}
唉,导航到/ Home / Index正确调用IndexAsync()方法并适当地提供Index.cshtml(而不是IndexAsync.cshtml)视图。路由到同步操作(例如“关于”)将照常处理。
答案 1 :(得分:0)
您应该制定约束以确保您的操作名称以Async
结尾,而不是尝试将文字与占位符放在一起。
routes.MapRoute(
name: "DefaultAsync",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "IndexAsync", id = UrlParameter.Optional },
constraints: new { action = @".*?Async" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);