为什么 Razor Pages 是在 Asp.net Core 中创建 Web UI 的推荐方法?

Why is Razor Pages the recommended approach to create a Web UI in Asp.net Core?

学习新事物需要投入时间、space和精力。我目前正在学习 Asp.Net Core MVC 2.0。这 ASP.NET Core tutorials overview 指出:

Razor Pages is the recommended approach to create a Web UI with ASP.NET Core

这些信息让我在决定是否必须停止学习 Asp.net Core MVC 并开始学习 Asp.net Core Razor Pages 时感到困惑。

欢迎任何指示。

Razor 页面针对基于页面的工作流进行了优化,并且可以在这些场景中使用,与传统 MVC 模型相比移动部件更少。这是因为您不需要处理 Controllers、Actions、Routes、ViewModels 和 Views(通常情况下)。相反,您的路线是基于约定的,并且您的 PageModel 充当您的控制器、动作和 ViewModel 合而为一。当然,页面会替换视图。您也不必像在 MVC 中那样拥有那么多文件夹,从而进一步简化您的项目。

来自ASP.NET Core - Simpler ASP.NET MVC Apps with Razor Pages, a Sept. 2017 MSDN article by Steve Smith

[Razor Pages] provide

  • a simpler way to organize code within ASP.NET Core applications, keeping implementation logic and view models closer to the view implementation code.
  • They also offer a simpler way to get started developing ASP.NET Core apps,

该文章详细介绍了为什么要在基于页面的工作流中使用 Razor Pages 而不是 MVC。显然,对于 API,您仍然希望使用控制器。

第 3 方编辑 - 经典 MVC 文件夹组织的缺点

ASP.NET Core - Feature Slices for ASP.NET Core MVC,2016 年 9 月的一篇较旧的 MSDN 文章,描述了为什么组织视图和控制器的经典 MVC 约定可能对大型项目不利。文章给出了四个松散相关的应用程序概念的示例:Ninjas、Plants、Pirates 和 Zombies。本文概述了一种在默认文件夹约定之外构建它们的方法,方法是按功能或责任区域将文件组织到文件夹中。

Microsoft 正在回归 WebForms 方法来简化项目结构,相信“约定优于配置”的口号,同时向开发人员隐藏配置以加快速度。但它有一个缺点,就是一切都会再次混合。这看起来不像是组织的明智之举。但是……嘿!新事物必须引起开发人员对 Microsoft 的注意。

如果您的页面使用 MVC Web API 作为 REStful,使用 Razor 页面确实更容易。如果没有,我建议您使用 Core MVC。

在大型项目中,模型和控制器一起在同一个文件中,维护将是一场噩梦。它适用于只有 2 个属性长的类,但它违反了 OOP 的开闭原则。您应该设计和使用一种可以随时间增长(可扩展)并且仍然稳定且符合逻辑(无需重新构建项目)的架构,只需使用相同的模式对其进行扩展即可。

来自 Microsoft 文档中的 this 文章:

MVC:使用控制器和视图,应用程序很常见 大型控制器 与许多不同的依赖项和视图模型一起工作并返回许多 不同的看法。这导致了很多复杂性,并且经常导致控制器不遵循 Single Responsibility PrincipleOpen/Closed Principles 有效。

Razor Pages 解决了这个问题 通过在 Web 应用程序中封装给定逻辑“页面”的服务器端逻辑来发布。没有服务器端逻辑的 Razor 页面可以只包含一个 Razor 文件(例如“Index.cshtml”)。然而,大多数重要的 Razor 页面都会有一个关联的页面模型 class,按照惯例,它的名称与扩展名为“.cs”的 Razor 文件相同(例如,“Index.cshtml.cs”)。此页面模型 class 结合了 Controller 和 ViewModel 的职责。与使用控制器操作方法处理请求不同,页面模型处理程序(如“OnGet()”)是 执行,默认呈现它们的关联页面。

Razor 页面简化了构建过程 ASP.NET 核心应用程序中的单个页面,同时仍提供 ASP.NET 核心 MVC 的所有架构功能。对于新的基于页面的功能,它们是一个很好的默认选择。

何时使用 MVC:

如果您正在构建 Web API,则 MVC 模式比尝试使用 Razor Pages 更有意义。 如果您的项目将仅公开 Web API 端点,理想情况下您应该从 Web API 项目开始 模板,否则很容易将控制器和关联的 API 端点添加到任何 ASP.NET 核心 应用程序。如果您正在迁移现有应用程序,您还应该使用基于视图的 MVC 方法 从 ASP.NET MVC 5 或更早版本到 ASP.NET Core MVC,并且您想用最少的 努力。完成初始迁移后,您可以评估采用它是否有意义 Razor Pages 的新功能或什至作为批发迁移。

注: 无论您选择使用 Razor Pages 还是 MVC 视图构建 Web 应用,您的应用都将具有 相似的性能并将包括对依赖项注入、过滤器、模型绑定、验证等的支持


更新: 我在 this github issue commented by scott sauber 上阅读的更多原因:

我们将 Razor Pages 用于 [复杂] 健康保险门户...我们有 60 多个页面,我可以说对于服务器呈现 HTML,我永远不会回到 MVC。它也不只是为了简单的事情。健康保险领域本身就很复杂,再加上它是一个多租户应用程序(我们将产品出售给其他保险公司)这一事实,这增加了复杂性,因为该应用程序具有高度可配置性,因为不同保险公司的做法略有不同.

为什么要用它?

  • Razor Pages 默认情况下更安全。 Razor Pages 默认为您提供 AntiForgeryToken 验证。此外,您还可以通过 [BindProperty] 选择要绑定哪些属性,这会限制您遭受过度发布攻击的风险。

  • 默认情况下,Razor Pages 具有更好的文件夹结构,可以更好地缩放。 在 MVC 中,默认文件夹结构根本无法缩放。当 Views、Controllers 和 ViewModels 最终紧密耦合时,拥有单独的文件夹是一个巨大的 PITA 工作。您最终会跳转到所有 3 个文件夹,并在需要添加或更改功能时随时浏览一堆。这太糟糕了。这就是我提倡功能文件夹的原因。使用 Razor Pages,您的 PageModel(Controller + ViewModel)与您的视图位于同一文件夹中。直接按F7切换也超级方便

  • 导致代码更易于维护,扩展性更好。 使用 MVC,使用 10 多个 Action 来膨胀控制器非常容易。通常,这些操作之间甚至没有任何关联(除了两者之间的重定向)。这使得在 Controller 中导航以查找代码变得非常困难。如果 Controller 中也有私有方法,情况会变得更糟,进一步增加方法膨胀。使用 Razor Pages,几乎不可能使用与页面无关的方法来膨胀页面模型。您放入 PageModel 中的所有内容都与您的页面相关。

  • 单元测试更容易。使用 Controller,您可能有 8 个 Action,而您注入的一些依赖项仅与一两个 Action 相关.因此,当对单个 Action 进行单元测试时,您要么需要不必要地模拟它们,要么传递一个 null,这两种方法都感觉很糟糕(这可以通过 Builder 模式稍微解决)。使用 Razor Pages,您注入的依赖项与您正在使用的 GET 和 POST 操作 100% 相关。感觉很自然。

  • 路由更容易。 默认情况下,在 Razor Pages 中,路由仅匹配您的文件夹结构。这使得嵌套文件夹更容易完成。例如,我们所有的人力资源管理页面都在 /Administrator 文件夹下,所有员工页面都在 /Employee 文件夹下。我们可以授权整个文件夹,并说此人必须是管理员才能访问 /Administrator 的任何子文件夹,这比使用构成管理员功能的多个控制器要容易得多。

我认为这很重要。


更新二:

这是关于 MVC 模式的一些复杂性,没有直接回答问题但可能很有用:Facebook 的一位工程经理说 (here) 对于他们“足够”的大型代码库和大型组织, “MVC 很快变得非常复杂,” 得出 MVC 无法扩展的结论。每次他们尝试添加新功能时,系统的复杂性都会呈指数级增长,从而使代码“脆弱且不可预测”。对于刚接触某个代码库的开发人员来说,这已成为一个严重的问题,因为他们害怕接触代码,以免破坏某些东西。结果是 MVC 在 Facebook 上分崩离析

2013 年开发者在论坛上提问 "What does Microsoft mean, Silverlight is not the recommended ...???" 只是这一次,是 MVC 将被宣告死亡,而 MVVM 万岁。 您可能会期望 MVC 被扔进废料堆,虽然速度很慢,但从现在起大约 18 个月后会加速,并且您花在学习 MVC 上的所有时间都将被扔进同一个废料堆。 此外,MVVM 看起来很简单,但需要一年时间才能掌握它并真正做到正确。

作为软件架构师,我自动使用设计模式。我最喜欢的是 Facade 设计模式。您将与 Home 相关的所有内容隐藏在 HomeController 之后,您可以对 Repositories 执行相同的操作。

Want to know a funny thing? A tour guide explained where the name Facade comes from. In Amsterdam you have big houses across the waters. From the outside they look luxureous. But from the behind they can be messy. The facade of the house hides whats behind it. Design patterns comes from the building world. Well whats behind in my applications also looks good but it was nice to know from the tour guide about the explanation.

如何支持 Razor 页面中的 共享分组 操作。如果查看 MVC 控制器,您会发现可以根据功能对控制器操作进行分组。您可以说主页就是这样一种功能。然后你有一个带有 About() 和 Contact() 的 HomeController,但是对于 Razor Pages,这将是不同的页面。可能你有一个很大的 HomeController,里面有 5 个其他视图。它们都可以分组在同一个 HomeController 中。

Controller 有两个 Razor Pages 没有的东西:

  1. 共享:可以在不同页面之间共享Controller动作,有时Controller动作并不只绑定到一个页面,而是可以在多个页面之间共享。请记住控制器操作也只能 return 数据 (JSON/XML/etc)。有时它们 return 也可以被不同的页面使用。
  2. 分组:您可以将相关的控制器操作组合在一个控制器中。好吧,如果你是小型控制器文件的粉丝,你就不会这样做。我愿意。我根据功能对控制器进行分组。这使导航更容易。

Razor 页面的处理方式是什么:我认为目录的使用:

  • 分组: 如果我们有 HomeController,那么我们可以创建一个包含所有主页的子目录 Home。

问题:对于一个简单的Home来说就足够了。但是假设我们有一个 XController,它对所有操作使用同一个存储库。您可以在 XController 的 Initializer 函数中初始化该存储库。但是对于 X 子目录中的页面,您必须对所有 X 操作再次执行此操作。那是干的吗?

  • 共享: 您可以创建一个 "Share" 子目录,在该子目录下,具有应在页面之间共享的功能的目录。

问题:如果您查看我的修复程序,您会发现我使用目录来解决 Razor 页面的共享和分组问题。

你会怎么做?

或者...Razor 页面是否仅适用于简单网站,这可能是此版本 Razor 页面的结论。

Blazor 服务器的架构很奇怪。它看起来像一个使用 SignalR 的聊天应用程序。我对此类应用程序的经验是事件可能会丢失。我不想丢失事件,最好是将它们堆叠起来并保证像邮件一样进行处理。