ASP.NET Core 2.2 中的端点路由无法正常工作
Endpoint Routing in ASP.NET Core 2.2 is Not Working
更新(2)
@poke 似乎已经弄清楚了,如果其他更高的路由存在歧义,那么它看起来是端点路由本身有利于 {*url}
的问题。
更新(1)
@poke 评论说我在 {*url}
路线上打错了 t 在 controller 中丢失了。修复 {*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
,但如果我只是在键盘上 bash 一些废话 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>
{action}
- doesn't work
这个不行,因为它必须与实际动作相匹配。所以它适用于 /Show
或 /List
,因为你在 PostsController
上操作。它也适用于 /
,因为 action
默认为 List
。
{*url}
- doesn't work
如果您设置默认 controller
而不是 conroller
:
,这个 将 工作
r.MapRoute("-1", "{*url}", new
{
action = "Gone",
controller = "Default"
});
<a asp-action="About" asp-controller="Default">About</a>
请注意,此路线也不会匹配,因为没有通往该操作的路线。 {controller}/{action}
路由被限制为 SettingsController
和 ToolsController
,因此路由不会匹配。您需要调整约束或添加另一条路线才能使其正常工作。
顺便说一句。作为一般性建议:您可能已经注意到,管理如此多的路由映射变得非常复杂。仅使用具有显式路由的属性路由通常更容易。您还可以将它们与基于模板的路由混合使用,以获得两全其美的效果。
Weirdly /settings
which is supposed to match the {controller}/{action}
route is now failing and falling back to the {*url}
route. If I remove the {*url}
route from the registrations then /settings
works again.
这似乎是 settings/{controller}/{action}
和 {controller}/{action=Default}
路线的副作用。
我现在已经调试了一段时间,这似乎是端点路由的一个错误,它有利于 catch all 路由,尽管它稍后会被注册。
不幸的是,ASP.NET Core 2.2 中的端点路由已知会在一些特殊情况下中断,这就是为什么要针对 3.0 对其进行改进,这有望解决所有问题。也就是说,我已经打开 an issue about this particular problem 并报告了我的发现。也许有一个简单的解决方案。
一个简单的解决方法是将 settings/{controller}/{action}
路由模板更改为使用 settings
以外的前缀,这样就不会再出现歧义。这似乎解决了问题。
更新(2)
@poke 似乎已经弄清楚了,如果其他更高的路由存在歧义,那么它看起来是端点路由本身有利于 {*url}
的问题。
更新(1)
@poke 评论说我在 {*url}
路线上打错了 t 在 controller 中丢失了。修复 {*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
,但如果我只是在键盘上 bash 一些废话 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>
{action}
- doesn't work
这个不行,因为它必须与实际动作相匹配。所以它适用于 /Show
或 /List
,因为你在 PostsController
上操作。它也适用于 /
,因为 action
默认为 List
。
{*url}
- doesn't work
如果您设置默认 controller
而不是 conroller
:
r.MapRoute("-1", "{*url}", new
{
action = "Gone",
controller = "Default"
});
<a asp-action="About" asp-controller="Default">About</a>
请注意,此路线也不会匹配,因为没有通往该操作的路线。 {controller}/{action}
路由被限制为 SettingsController
和 ToolsController
,因此路由不会匹配。您需要调整约束或添加另一条路线才能使其正常工作。
顺便说一句。作为一般性建议:您可能已经注意到,管理如此多的路由映射变得非常复杂。仅使用具有显式路由的属性路由通常更容易。您还可以将它们与基于模板的路由混合使用,以获得两全其美的效果。
Weirdly
/settings
which is supposed to match the{controller}/{action}
route is now failing and falling back to the{*url}
route. If I remove the{*url}
route from the registrations then/settings
works again.
这似乎是 settings/{controller}/{action}
和 {controller}/{action=Default}
路线的副作用。
我现在已经调试了一段时间,这似乎是端点路由的一个错误,它有利于 catch all 路由,尽管它稍后会被注册。
不幸的是,ASP.NET Core 2.2 中的端点路由已知会在一些特殊情况下中断,这就是为什么要针对 3.0 对其进行改进,这有望解决所有问题。也就是说,我已经打开 an issue about this particular problem 并报告了我的发现。也许有一个简单的解决方案。
一个简单的解决方法是将 settings/{controller}/{action}
路由模板更改为使用 settings
以外的前缀,这样就不会再出现歧义。这似乎解决了问题。