【发布时间】:2020-11-10 21:50:29
【问题描述】:
关于 ASP.NET Core 最受欢迎的书籍之一是 Adam Freeman 的 Pro ASP.NET Core 3。
在第 7-11 章中,他构建了一个示例应用程序 SportsStore:
在左侧,您会注意到各种产品类别(“国际象棋”、“足球”等)的按钮。这些链接由以下代码生成:
@foreach (string category in Model) {
<a class="btn btn-block
@(category == ViewBag.SelectedCategory
? "btn-primary": "btn-outline-secondary")"
asp-action="Index" asp-controller="Home"
asp-route-category="@category"
asp-route-productPage="1">
@category
</a>
}
Views/Shared/Components/NavigationMenu/Default.cshtml
对动作的类型安全引用
这部分:
asp-action="Index"
指的是HomeController中的Index方法。但是,这段代码不是类型安全的;如果我们有错字:
asp-action="IndexAbc"
项目仍在编译。
好的,不用担心,我们可以解决这个问题:
asp-action="@nameof(HomeController.Index)"
我们去吧。现在像下面这样的错字会在编辑时立即给我们一个错误,项目当然不会编译:
asp-action="@nameof(HomeController.IndexAbc)"
对控制器的类型安全引用
对控制器的引用也不是类型安全的:
asp-controller="Home"
解决这个比较冗长,但可行:
asp-controller="@Regex.Replace(nameof(HomeController), "Controller$", String.Empty)"
同样,现在控制器名称中的拼写错误会在编译时被捕获。如果控制器被重命名,这也会捕获它。
类型安全的路由参数
以类型安全的方式处理路由值怎么样?以下是我们正在处理的两个参数和值:
asp-route-category="@category"
asp-route-productPage="1"
我们不能像前面的例子那样真正使用nameof;没有什么可以真正使用nameof。
这是Index 方法签名:
public ViewResult Index(string category, int productPage = 1)
这几乎就像我们希望能够说 nameof(productPage) 但我们无法访问视图组件中的 productPage 参数。
让我们退后一步......
以下代码:
<a class="btn btn-block @(category == ViewBag.SelectedCategory ? "btn-primary" : "btn-outline-secondary")"
asp-action="Index"
asp-controller="Home"
asp-route-category="@category"
asp-route-productPage="1">
@category
</a>
最终被扩展成这样的:
<a class="btn btn-block btn-outline-secondary" href="/Chess/Page1">
Chess
</a>
特别是以下几行:
asp-action="Index"
asp-controller="Home"
asp-route-category="@category"
asp-route-productPage="1"
被压扁成这样:
href="/Chess/Page1"
ASP.NET Core 允许我们使用 LinkGenerator.GetPathByAction 方法执行此映射 programmatically。
例如,下面的调用:
_linkGenerator.GetPathByAction("Index", "Home", new { category = "Chess", productPage = 1 })
为我们提供以下信息:
"/Chess/Page1"
我们现在非常接近。现在的挑战是:
new { category = "Chess", productPage = 1 }
不是类型安全的。以下拼写错误不会阻止项目的构建:
new { categoryAbc = "Chess", productPageXyz = 1 }
让我们改变Index方法来接收一个对象作为参数:
public ViewResult Index(IndexParameters parameters)
IndexParameters 在哪里:
public class IndexParameters
{
public string Category { get; set; }
public int ProductPage { get; set; } = 1;
}
现在我们可以以类型安全的方式传递参数值了:
_linkGenerator.GetPathByAction("Index", "Home", new IndexParameters() { Category = category, ProductPage = 1 }))
完整的类型安全示例
好的,现在我们已经到达了完整的类型安全 a 标签:
<a class="btn btn-block @(category == ViewBag.SelectedCategory ? "btn-primary" : "btn-outline-secondary")"
href="@(
_linkGenerator.GetPathByAction(
nameof(HomeController.Index),
Regex.Replace(nameof(HomeController), "Controller$", String.Empty),
new IndexParameters()
{
Category = category,
ProductPage = 1
}))">
@category
</a>
我已将以下项目添加到此新版本代码的视图组件文件中:
@using SportsStore.Controllers
@using System.Text.RegularExpressions
@using Microsoft.AspNetCore.Routing
@inject LinkGenerator _linkGenerator
我的问题是......有没有更简洁的方法可以在相同的代码中获得相同级别的类型安全?还是我真的必须跳过所有这些障碍才能使代码类型安全?
【问题讨论】:
-
如果类型安全对你很重要,我只能想办法,那就是完全在服务器上,在Controller中创建链接,并通过视图模型传递给视图。
-
我在最后更新了完整的示例,使其成为多行而不是全部在一行。这使它看起来更好一点。
-
@DavidLiang 据我所知,通过上述方法,代码完全在服务器上运行,虽然不完全在控制器中;其中一些是在视图组件中完成的。
-
是的,我明白了。我想我的意思是,不是在视图上使用标签助手来生成带有参数的链接,而是在控制器中生成链接并将它们作为字符串传递回视图......
-
@DavidLiang 有人建议我研究一下 R4MVC 项目。我添加了一个引用该项目的答案。听起来很有希望!
标签: asp.net-core asp.net-core-mvc asp.net-core-3.1 strong-typing