【问题标题】:Make ASP.net MVC 5 Routing Ignore Async Suffix on Action methods使 ASP.net MVC 5 Routing Ignore Async Suffix on Action 方法
【发布时间】: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}/{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 方法(这是异步但不遵循 Async 命名约定)并将其映射到相应的 MyAction 方法,这将很有用。

【问题讨论】:

  • 您是否将您发布的路线放在默认路线之前?
  • @NightOwl888 - 是的,之前订购过。我已更新帖子以更好地描述实际问题。
  • 所以,我认为你最初所说的,你的 xxxAsync 操作成功是不正确的?您的更新与您最初所说的直接矛盾。
  • @NightOwl888 - 不,它是正确的。当 URL 包含 &lt;action&gt;Async 而不是当 URL 仅包含 &lt;action&gt; 时,它会成功(即返回 200)。我的目标是让 MVC 看到方法 &lt;action&gt;Async 对应于从 URL 检索到的 &lt;action&gt; 所以我不需要(a)用动作名称装饰无数方法,或者(b)违反异步命名约定。

标签: asp.net-mvc asp.net-web-api asp.net-mvc-5 asp.net-mvc-routing asp.net-web-api2


【解决方案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), $"{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)视图。同步操作(例如“关于”)的路由按正常方式处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多