【问题标题】:Endpoint Routing in ASP.NET Core 2.2 is Not WorkingASP.NET Core 2.2 中的端点路由不起作用
【发布时间】:2019-04-25 18:17:01
【问题描述】:

更新(2)

@poke 似乎已经弄清楚了,如果与其他更高的路由存在歧义,那么它似乎是端点路由本身偏爱{*url} 的问题。

更新(1)

@poke 评论说我在{*url} 路由上有一个错字,其中 controller 中的 t 丢失了。在修复 {*url} 路由开始工作并且 DefaultController.Gone 操作开始工作后。

但是! 现在奇怪的行为又开始出现了。修复 {*url} 后,导航到应该与 {controller}/{action} 路由匹配的 /settings 失败并回退到 {*url} 路由。

如果我从注册中删除 {*url} 路由,那么 /settings 将再次起作用。 {action} 路由仍然不起作用。

原创

请原谅问题的篇幅,但我试图提供尽可能多的信息。

我正在为自己开发一个 ASP.NET Core 2.2 博客应用程序,但在路由正常工作时遇到了莫名其妙的问题。在对着屏幕大喊大叫半天之后,我决定退后一步,开始一个完全孤立的新项目。不知何故,问题在新项目中仍然存在。我已经把它剥离成一个饥饿的骨架,但我仍然无法让路线工作。我正在尝试设置的路线是:

settings/{controller}/{id:int}/{action} - works
settings/{controller}/{action} - works
blog/{*slug} - works
blog/{skip:int?} - works
{controller}/{action} - works
{action} - doesn't work
{*url} - doesn't work

具体来说,我在最后两条路线上遇到了问题。

{action} 路由不会为像 DefaultController.About 这样的简单操作生成,即使它没有约束,它所拥有的只是 PostsController.List 的默认值,因为我希望为根 URL 显示帖子列表。

{*url} 似乎根本不起作用。我想将它用作我的最终后备,它默认为DefaultController.Gone,但如果我只是在键盘上敲击一些无意义的 URL,我得到的只是 404 错误。

我觉得问题出在DefaultController 上,因为About 和Gone 动作都在其中,而且似乎都没有工作,但我似乎不知道怎么做。它实际上什么都不做,只是渲染视图,就像其他控制器一样。

下面是精简项目的代码。如果有人能启动它并告诉我哪里失败了,我将非常感激,因为我似乎无法弄清楚。

Program.cs

public sealed class Program {
    public static async Task Main(
        string[] args) => await WebHost.CreateDefaultBuilder(args)
                                       .UseStartup<Startup>()
                                       .Build()
                                       .RunAsync();
}

Startup.cs

public class Startup {
    public void ConfigureServices(
        IServiceCollection services) {
        services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Latest);
    }

    public void Configure(
        IApplicationBuilder app) {
        app.UseMvc(
            r => {
                //  /settings/{controller}/{id}/{action}
                r.MapRoute("600", "settings/{controller}/{id:int}/{action}", null, new {
                    controller = "Categories|Tags"
                });

                //  /settings/{controller}/{action}
                r.MapRoute("500", "settings/{controller}/{action}", null, new {
                    controller = "Categories|Tags"
                });

                //  /blog/*
                r.MapRoute("400", "blog/{*slug}", new {
                    action = "Show",
                    controller = "Posts"
                });

                //  /blog/{skip}
                r.MapRoute("300", "blog/{skip:int?}", new {
                    action = "List",
                    controller = "Posts"
                });

                //  /{controller}/{action}
                r.MapRoute("200", "{controller}/{action=Default}", null, new {
                    controller = "Settings|Tools"
                });

                //  /{action}
                r.MapRoute("100", "{action}", new {
                    action = "List",
                    controller = "Posts"
                });

                //  /*
                r.MapRoute("-1", "{*url}", new {
                    action = "Gone",
                    conroller = "Default"
                });
            });
    }
}

CategoriesController.cs

public sealed class CategoriesController :
    Controller {
    [HttpGet]
    public IActionResult Add() => Content("Category added");

    [HttpGet]
    public IActionResult Remove(
        int id) => Content($"Category {id} removed");
}

DefaultController.cs

public sealed class DefaultController :
    Controller {
    [HttpGet]
    public IActionResult About() => View();

    [HttpGet]
    public IActionResult Gone() => View();
}

About.cshtml(默认)

<h1>DEFAULT.ABOUT</h1>

Gone.cshtml(默认)

<h1>DEFAULT.GONE</h1>

PostsController.cs

public sealed class PostsController :
    Controller {
    [HttpGet]
    public IActionResult List(
        int? skip) => View();

    [HttpGet]
    public IActionResult Show(
        string slug) => View();
}

List.cshtml(帖子)

<h1>POSTS.LIST</h1>
<a asp-action="Show" asp-controller="Posts" asp-route-slug="test-test-test">Show a Post</a>

Show.cshtml(帖子)

<h1>POSTS.SHOW</h1>

SettingsController.cs

public sealed class SettingsController :
    Controller {
    [HttpGet]
    public IActionResult Default() => View();
}

Default.cshtml(设置)

<h1>SETTINGS.DEFAULT</h1>
<a asp-action="Add" asp-controller="Categories">Add a Category</a>
<br />
<a asp-action="Remove" asp-controller="Categories" asp-route-id="1">Remove a Category</a>
<hr />
<a asp-action="Add" asp-controller="Tags">Add a Tag</a>
<br />
<a asp-action="Remove" asp-controller="Tags" asp-route-id="1">Remove a Tag</a>

TagsController.cs

public sealed class TagsController :
    Controller {
    [HttpGet]
    public IActionResult Add() => Content("Tag added");

    [HttpGet]
    public IActionResult Remove(
        int id) => Content($"Tag {id} removed");
}

ToolsController.cs

public sealed class ToolsController :
    Controller {
    [HttpGet]
    public IActionResult Default() => View();
}

Default.cshtml

<h1>TOOLS.DEFAULT</h1>

_Layout.cshtml

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
</head>
<body>
    <a asp-action="List" asp-controller="Posts">Blog</a>
    <br />
    <a asp-action="Default" asp-controller="Tools">Tools</a>
    <br />
    <a asp-action="About" asp-controller="Default">About</a>
    <br />
    <a asp-action="Default" asp-controller="Settings">Settings</a>
    <br />
    @RenderBody()
</body>
</html>

【问题讨论】:

  • conroller = "Default" - 有一个错字,t 不见了。这能解决吗? ??????
  • @poke,谢谢,它修复了{*url} 路由,但{action} 路由仍然没有生成。奇怪的 /settings 应该匹配 {controller}/{action} 路由现在失败并回退到 {*url} 路由。如果我从注册中删除{*url} 路由,那么/settings 将再次起作用。我开始回想起原始项目中奇怪的行为。 :(

标签: c# asp.net-core routing asp.net-core-2.2


【解决方案1】:

{action} - 不起作用

这个不起作用,因为它必须与实际动作相匹配。所以它适用于/Show 或/List,因为您在PostsController 上进行操作。它也适用于/,因为action 默认为List。

{*url} - 不起作用

如果您设置默认的controller,而不是conroller,这个将工作:

r.MapRoute("-1", "{*url}", new
{
    action = "Gone",
    controller = "Default"
});

&lt;a asp-action="About" asp-controller="Default"&gt;About&lt;/a&gt;

请注意,此路由也不会匹配,因为没有指向该操作的路由。 {controller}/{action} 路由被限制为 SettingsController 和 ToolsController,因此路由将不匹配。您需要调整约束或添加另一条路线才能使其正常工作。

顺便说一句。作为一般建议:您可能已经注意到,管理如此多的路由映射变得相当复杂。将属性路由与显式路由一起使用通常更容易。您还可以将它们与基于模板的路由混合使用,以获得两全其美的效果。


奇怪的 /settings 应该匹配 {controller}/{action} 路由现在失败并回退到 {*url} 路由。如果我从注册中删除 {*url} 路由,那么 /settings 将再次起作用。

这似乎是合并 settings/{controller}/{action} 和 {controller}/{action=Default} 路由的副作用。

我已经调试了一段时间,这似乎是端点路由的一个错误,它有利于捕获所有路由,尽管它稍后会被注册。

不幸的是,众所周知,ASP.NET Core 2.2 中的端点路由在一些特殊情况下会中断,这就是为什么要针对 3.0 进行改进,希望能够解决所有问题。话虽如此,我已经打开了an issue about this particular problem 并报告了我的发现。也许有一个简单的解决方案。

一个简单的解决方法是将settings/{controller}/{action} 路由模板更改为使用settings 以外的前缀,这样就不再有歧义。这似乎可以解决问题。

【讨论】:

  • 我想我开始看到了。由于{action} 路由有一个默认控制器Posts,所以我在标签助手中指定控制器并不重要,因为即使我想要Default.About,生成的路由也会是Posts.About,因为操作不存在,链接为空白。添加属性修复,所以我会承认。话虽如此,{*url} 以某种方式中断导航到/settings 仍然是一个令人困惑的问题。
  • 我决定移除 {action} 路由注册,并在 PostsController.List 操作上添加了 "" 和 "blog" 的属性,它涵盖了根 URL 和 /blog URL。我仍然不明白为什么 /settings 路由到 {*url} 路由时 {controller}/{action} 路由应该首先匹配基于约束(和注册顺序?)。它适用于/tools。删除{*url} 路由修复/settings。猜猜它为什么会这样?
  • “即使我想要 Default.About,生成的路由也会是 Posts.About”——这并不完全正确。路由的工作方式是它为前面的所有端点生成 URL,因此它已经知道要使用哪个路由,例如PostsController.List。但它无法为Default.About 生成 URL,因为没有匹配的路由模板。因此,当您尝试为其生成 URL 时,它根本就失败了。
  • 至于/settings的路线,我会尝试看看我以后是否可以跟进。目前,一种解决方法是更改​​/settings/{controller}/{action} 路由以使用与settings 不同的前缀。这消除了歧义并至少使路线正常工作。
  • 感谢您抽出宝贵时间进行调查,非常感谢。看起来我仍然有办法使用端点路由......我选择了不同的解决方法并将兼容性版本设置为 2.1,它现在可以正确路由。如果您要打开一个问题,您可以在这里链接它吗?我也想追。再次感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-12
  • 1970-01-01
  • 2019-05-14
  • 1970-01-01
相关资源
最近更新 更多