3

假设我有一个 ASP.NET Core 3.0 MVC 应用程序,它具有一个简单的控制器,其中包含两个操作并使用基于属性的路由:

[Route("home")]
public class HomeController : Controller
{
    public static string ControllerName { get; } = "Home";

    public HomeController()
    {
    }

    string GenerateUrls()
    {
        string url1 = Url.Action(nameof(Action1), ControllerName);
        string url2 = Url.Action(nameof(Action2Async), ControllerName);
        return $"Action1: '{url1}'\nAction2: '{url2}'";
    }

    [HttpGet("a1")]
    public IActionResult Action1()
    {
        return Ok(GenerateUrls());
    }

    [HttpGet("a2")]
    public async Task<IActionResult> Action2Async()
    {
        await Task.CompletedTask;

        return Ok(GenerateUrls());
    }
}

因此,调用任一操作应该只生成一个显示这两个操作的 URL 的页面。

打开/home/a1/home/a2正确调用相应的操作,但输出有点出乎意料:

Action1: '/home/a1'
Action2: ''

这表明它Url.Action()为第二个动作返回了一个空字符串,而对于第一个动作它工作得很好。

在调试了一段时间后,我发现一篇博客文章将这个问题追踪到 ASP.NET Core 3.0 中的一个重大变化,其中Async后缀不知何故被Url.Action().

作者通过将字符串硬编码为操作名称(在我的情况下为"Action1"und )解决了这个问题。"Action2"他还上传了一些重现此行为的示例代码。

但是,我真的更愿意保留nameof, 以避免以后的重命名/重构问题。

是否有一种干净的方式来使用nameof或其他类型安全的构造来为函数提供带有Async后缀的方法Url.Action

4

2 回答 2

4

所描述的行为是由 ASP.NET Core 3.0 引入的重大更改引起的。

您可以通过禁用返回旧行为SuppressAsyncSuffixInActionNames

获取或设置一个值,该值确定 MVC 是否将删除应用于控制器操作名称的后缀“Async”。

AddControllers在配置应用程序服务时在您的呼叫中禁用此开关:

services.AddControllers(options => {
    options.SuppressAsyncSuffixInActionNames = false;
});

您可以在官方公告文档中找到有关此更改的更多信息。

于 2020-01-29T21:47:44.160 回答
-1

如果您将方法名称 Action2Async 更改为 Action2,问题将得到解决。

从链接的博客文章

在 ASP.NET Core 3 中,如果您有以 Async 为后缀的 Action 方法,但路由路径不包含 Async,请在通过类似 Url.Action() 解析 URL 时引用它们而不使用 Async 后缀。这似乎是 ASP.NET Core 2.2 的一个重大变化,没有正式记录。

public static string ControllerName { get; } = "Home";

string GenerateUrls()
{
    string url1 = Url.Action(nameof(Action1), ControllerName);
    string url2 = Url.Action(nameof(Action2), ControllerName);
    return $"Action1: '{url1}'\nAction2: '{url2}'";
}

[HttpGet("action1")]
public IActionResult Action1()
{
    return Ok(GenerateUrls());
}

[HttpGet("action2")]
public async Task<IActionResult> Action2()
{
    await Task.CompletedTask;

    return Ok(GenerateUrls());
}
于 2020-01-29T15:21:51.273 回答