使 ASP.net MVC 5 Routing Ignore Async Suffix on Action 方法
Posted
技术标签:
【中文标题】使 ASP.net MVC 5 Routing Ignore Async Suffix on Action 方法【英文标题】:Make ASP.net MVC 5 Routing Ignore Async Suffix on Action methods 【发布时间】:2016-06-07 09:25:19 【问题描述】: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/actionAsync/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
方法(这是异步但不遵循 Async 命名约定)并将其映射到相应的 MyAction
方法,这将很有用。
【问题讨论】:
可能相关***.com/questions/22664924/… 您是否将您发布的路线放在默认路线之前? @NightOwl888 - 是的,之前订购过。我已更新帖子以更好地描述实际问题。 所以,我认为你最初所说的,你的 xxxAsync 操作成功是不正确的?您的更新与您最初所说的直接矛盾。 @NightOwl888 - 不,它是正确的。当 URL 包含<action>Async
而不是当 URL 仅包含 <action>
时,它会成功(即返回 200)。我的目标是让 MVC 看到方法 <action>Async
对应于从 URL 检索到的 <action>
所以我不需要(a)用动作名称装饰无数方法,或者(b)违反异步命名约定。
【参考方案1】:
不要尝试将文字与占位符放在一起,您应该进行约束以确保您的操作名称以 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
);
【讨论】:
约束正则表达式似乎关闭,因为它正在寻找零个或多个文字点。 @KalebPederson 我不这么认为,这意味着在零和无限字符之间,但是我认为“.+Async”会更好。 @NightOwl888 你怎么看? 这篇文章是根据我的原始评论编辑的。现在是合理的。【参考方案2】:一开始,我对 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), $"actionNameAsync");
return asyncAction != null
? base.BeginInvokeAction(controllerContext, $"actionNameAsync", callback, state)
: base.BeginInvokeAction(controllerContext, actionName, callback, state);
唉,导航到 /Home/Index 会正确调用 IndexAsync() 方法并适当地提供 Index.cshtml(不是 IndexAsync.cshtml)视图。同步操作(例如“关于”)的路由按正常方式处理。
【讨论】:
以上是关于使 ASP.net MVC 5 Routing Ignore Async Suffix on Action 方法的主要内容,如果未能解决你的问题,请参考以下文章