19 Creating a website with MVC controllers
19 使用 MVC 控制器创建网站
This chapter covers
本章涵盖
• Creating a Model-View-Controller (MVC) application
创建模型-视图-控制器 (MVC) 应用程序
• Choosing between Razor Pages and MVC controllers
在 Razor Pages 和 MVC 控制器之间进行选择
• Returning Razor views from MVC controllers
从 MVC 控制器返回 Razor 视图
In this book I’ve focused on Razor Pages over MVC controllers for server-rendered HTML apps, as I consider Razor Pages to be the preferable paradigm in most cases. In this chapter we dig a bit more into exactly why I consider Razor Pages to be the right choice and take a brief look at the alternative.
在这本书中,我重点介绍了服务器渲染的 HTML 应用程序的 Razor Pages 而不是 MVC 控制器,因为我认为在大多数情况下,Razor Pages 是更可取的范例。在本章中,我们将更深入地探讨为什么我认为 Razor Pages 是正确的选择,并简要介绍一下替代方案。
In section 19.2 you’ll create a default MVC application using a template so you can familiarize yourself with the general project layout of an MVC application. We’ll look at some of the differences between an MVC application and a Razor Pages app, as well as the many similarities.
在 Section 19.2 中,您将使用模板创建默认的 MVC 应用程序,以便熟悉 MVC 应用程序的一般项目布局。我们将了解 MVC 应用程序和 Razor Pages 应用程序之间的一些差异,以及许多相似之处。
Next, I’ll dig into why I find Razor Pages to be a preferable application model compared with MVC controllers. You’ll learn about the improved developer ergonomics of Razor Pages compared with MVC controllers, as well as the cases in which MVC controllers are nevertheless the right choice.
接下来,我将深入探讨为什么我认为与 MVC 控制器相比,Razor Pages 是更可取的应用程序模型。您将了解 Razor Pages 与 MVC 控制器相比改进的开发人员人体工程学,以及 MVC 控制器仍然是正确选择的情况。
In section 19.4 you’ll learn about rendering Razor views using MVC controllers. You’ll learn how the MVC framework relies on conventions to locate view files and how to override these by selecting a specific Razor view template to render. Finally, you’ll see the full view selection algorithm in all its glory.
在第 19.4 节中,您将了解如何使用 MVC 控制器渲染 Razor 视图。您将了解 MVC 框架如何依赖约定来查找视图文件,以及如何通过选择要呈现的特定 Razor 视图模板来覆盖这些文件。最后,您将看到完整视图选择算法的所有荣耀。
19.1 Razor Pages vs. MVC in ASP.NET Core
19.1 Razor Pages 与 ASP.NET Core 中的 MVC
In this book I focus on Razor Pages, but I have also mentioned that Razor Pages use the ASP.NET Core MVC framework behind the scenes and that you can choose to use the MVC framework directly if you wish. Additionally, if you’re creating an API for working with mobile or client-side apps, and you don’t want to (or can’t) use minimal APIs, you may well use the MVC framework directly by creating web API controllers.
在本书中,我重点介绍了 Razor Pages,但我也提到了 Razor Pages 在后台使用 ASP.NET Core MVC 框架,如果您愿意,可以选择直接使用 MVC 框架。此外,如果您正在创建用于移动或客户端应用程序的 API,并且您不想(或不能)使用最少的 API,则可以通过创建 Web API 控制器来直接使用 MVC 框架。
NOTE I look at how to build web APIs with the MVC framework in chapter 20.
注意:在第 20 章中,我将介绍如何使用 MVC 框架构建 Web API。
So what are the differences between Razor Pages and the MVC framework, and when should you choose one or the other?
那么 Razor Pages 和 MVC 框架有什么区别,什么时候应该选择其中之一呢?
If you’re new to ASP.NET Core, the answer is pretty simple: use Razor Pages for server-side rendered applications, and use web API controllers (or minimal APIs) for building APIs. There are nuances to this advice, which I discuss in section 19.5, but that distinction will serve you well for now.
如果你不熟悉 ASP.NET Core,答案非常简单:将 Razor Pages 用于服务器端呈现的应用程序,并使用 Web API 控制器(或最小 API)来构建 API。这个建议有一些细微差别,我在第 19.5 节中讨论,但这种区别现在对你很有帮助。
Naming is hard, again
命名很困难,同样Microsoft have a long history of creating a framework and naming it after a generic concept: MVC, Web Forms, Web Pages, Multi-platform App UI, and so on. it’s frankly incredible that Blazor survived! Web API is no different.
Microsoft 创建框架并以通用概念命名它的历史由来已久:MVC、Web Forms、Web Pages、Multi-platform App UI 等。坦率地说,Blazor 幸存下来真是不可思议!Web API 也不例外。In legacy ASP.NET, Microsoft created a web API framework, which was similar in design to the existing MVC framework, but also was not interoperable. You therefore had MVC controllers, which were controller classes used with the MVC framework to generate HTML, and web API controllers, which were controller classes used with the web API framework, to generate JavaScript Object Notation (JSON) or Extensible Markup Language (XML).
在旧版 ASP.NET 中,Microsoft 创建了一个 Web API 框架,该框架在设计上与现有的 MVC 框架相似,但也不可互作。因此,您有 MVC 控制器(与 MVC 框架一起使用的控制器类,用于生成 HTML)和 Web API 控制器(与 Web API 框架一起使用的控制器类),用于生成 JavaScript 对象表示法 (JSON) 或可扩展标记语言 (XML)。In ASP.NET Core, Microsoft merged these two parallel stacks into a single ASP.NET Core MVC framework. Controllers in ASP.NET Core can generate both HTML and JSON/XML; there is no separation. Nevertheless, it’s common for a controller to be dedicated to either HTML generation or JSON/XML. For that reason, the names MVC controller and web API controller are often used to refer to the two general types of controller: MVC for HTML and web API for JSON/XML.
在 ASP.NET Core 中,Microsoft将这两个并行堆栈合并到一个 ASP.NET Core MVC 框架中。ASP.NET Core 中的控制器可以生成 HTML 和 JSON/XML;没有分离。尽管如此,控制器通常专用于 HTML 生成或 JSON/XML。因此,MVC 控制器和 Web API 控制器这两个名称通常用于指代两种常规类型的控制器:用于 HTML 的 MVC 和用于 JSON/XML 的 Web API。In this book when I refer to web API controllers, I’m talking about standard ASP.NET Core controllers that are generating API responses. This may be described elsewhere as a web API application using MVC controllers or as a web API application. All three cases refer to the same concept: an HTTP API built using ASP.NET Core controllers.
在本书中,当我提到 Web API 控制器时,我指的是生成 API 响应的标准 ASP.NET Core 控制器。这可能在其他地方描述为使用 MVC 控制器的 Web API 应用程序或 Web API 应用程序。这三种情况都是指同一个概念:使用 ASP.NET Core 控制器构建的 HTTP API。
Before we can get to comparisons, though, we should take a brief look at the ASP.NET Core MVC framework itself. Understanding the similarities and differences between MVC controllers and Razor Pages can be useful, as you’ll likely find a use for MVC controllers at some point, even if you use Razor Pages most of the time.
不过,在进行比较之前,我们应该简要了解一下 ASP.NET Core MVC 框架本身。了解 MVC 控制器和 Razor Pages 之间的异同可能很有用,因为即使您大部分时间都在使用 Razor Pages,您也可能会在某些时候发现 MVC 控制器的用途。
19.2 Your first MVC web application
19.2 您的第一个 MVC Web 应用程序
In this section you’ll learn how to create your first MVC web application, which server-renders HTML pages using MVC controllers and Razor views. We use a template to create the app and compare the generated code to see how it differs from a Razor Pages application.
在本部分中,你将了解如何创建第一个 MVC Web 应用程序,该应用程序使用 MVC 控制器和 Razor 视图服务器呈现 HTML 页面。我们使用模板创建应用并比较生成的代码,以了解它与 Razor Pages 应用程序有何不同。
We’ll again use a template to get an application up and running quickly. This time we’ll use the ASP.NET Core Web App (Model-View-Controller) template. To create the application in Visual Studio, follow these steps:
我们将再次使用模板来快速启动和运行应用程序。这次,我们将使用 ASP.NET Core Web 应用程序 (Model-View-Controller) 模板。要在 Visual Studio 中创建应用程序,请执行以下步骤:
- Choose File > New.
选择 File > New (文件新建)。 - In the Create a new project dialog box, select the ASP.NET Core Web App (Model-View-Controller) template.
在 Create a new project (创建新项目) 对话框中,选择 ASP.NET Core Web App (Model-View-Controller) 模板。 - In the Create a new project dialog box, enter your project name and review the Additional information box, shown in figure 19.1.
在 Create a new project 对话框中,输入您的项目名称并查看 Additional information 框,如图 19.1 所示。 - Choose Create. If you’re using the command-line interface (CLI), you can create a similar template using dotnet new mvc.
选择 Create (创建)。如果您使用的是命令行界面 (CLI),则可以使用 dotnet new mvc 创建类似的模板。
Figure 19.1 The Additional information screen for the MVC template. This screen follows on from the Configure your new project dialog box and lets you customize the template that generates your application.
图 19.1 MVC 模板的 Additional information 屏幕。此屏幕是 Configure your new project 对话框的后续屏幕,允许您自定义生成应用程序的模板。
The MVC template configures the ASP.NET Core project to use MVC controllers with Razor views. As always, you configure your app to use MVC controllers in Program.cs, as shown in listing 19.1. If you compare this template with your Razor Pages projects, you’ll see that the web API project uses AddControllersWithViews() instead of AddRazorPages(). The MVC controllers are mapped as endpoints by calling MapControllerRoute(). This method maps all the controllers in your app and configures a default conventional route for them. We discussed conventional routing in chapter 14, and I will discuss it again briefly shortly.
MVC 模板将 ASP.NET Core 项目配置为将 MVC 控制器与 Razor 视图一起使用。与往常一样,您将应用程序配置为在 Program.cs中使用 MVC 控制器,如清单 19.1 所示。如果将此模板与 Razor Pages 项目进行比较,你将看到 Web API 项目使用 AddControllersWithViews() 而不是 AddRazorPages()。通过调用 MapControllerRoute() 将 MVC 控制器映射为端点。此方法映射应用程序中的所有控制器,并为它们配置默认的常规路由。我们在第 14 章中讨论了 conventional routing,稍后我将再次简要讨论它。
Listing 19.1 Program.cs for the default MVC project
清单 19.1 默认 MVC 项目的 Program.cs
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(); #A
WebApplication app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error"); #B
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute( #C
name: "default", #C
pattern: "{controller=Home}/{action=Index}/{id?}"); #D
app.Run();
❶ AddControllersWithViews adds the services for MVC controllers with Razor Views.
AddControllersWithViews 为具有 Razor 视图的 MVC 控制器添加了服务。
❷ The exception handler path differs from the default Razor Pages path of /Error.
异常处理程序路径不同于默认的 Razor Pages 路径 /Error。
❸ Adds all MVC controllers in your application using conventional routing
使用常规路由在应用程序中添加所有 MVC 控制器
❹ Defines the default conventional route pattern
定义默认的常规路由模式
Much of the configuration for an MVC application is the same as for Razor Pages. The middleware configuration is essentially identical, which isn’t that surprising considering that MVC and Razor Pages are the same type of application: a server-rendered app returning HTML. The main difference, as you’ll see in section 19.3, is in the project structure.
MVC 应用程序的大部分配置与 Razor Pages 的配置相同。中间件配置本质上是相同的,考虑到 MVC 和 Razor Pages 是同一类型的应用程序:返回 HTML 的服务器渲染应用程序,这并不奇怪。正如您将在 19.3 节中看到的那样,主要区别在于项目结构。
Before we go any further, run the MVC application by pressing F5 in Visual Studio or by running dotnet run in the project folder. The application should look remarkably familiar; it’s essentially identical to the Razor Pages version of the application you created in chapter 13, as shown in figure 19.2.
在继续之前,请在 Visual Studio 中按 F5 或在项目文件夹中运行 dotnet run 来运行 MVC 应用程序。该应用程序看起来应该非常熟悉;它与您在第 13 章中创建的应用程序的 Razor Pages 版本基本相同,如图 19.2 所示。
Figure 19.2 The default MVC application. The resulting application is identical to the Razor Pages equivalent created in chapter 13.
图 19.2 默认的 MVC 应用程序。生成的应用程序与第 13 章中创建的 Razor Pages 等效项相同。
The output of the MVC app is identical to the default Razor Pages app, but the infrastructure used to generate the response differs. Instead of a Razor Page PageModel and page handler, MVC uses the concept of controllers and action methods. The following listing shows the HomeController class from the default application. Each nonabstract, public method is an action that runs in response to a request. You can ensure that a candidate method is not treated as an action method by decorating it with the [NonAction] attribute.
MVC 应用的输出与默认的 Razor Pages 应用相同,但用于生成响应的基础结构不同。MVC 使用控制器和作方法的概念,而不是 Razor Page PageModel 和页面处理程序。下面的清单显示了默认应用程序中的 HomeController 类。每个非抽象的公共方法都是为响应请求而运行的作。您可以通过使用 [NonAction] 属性修饰候选方法,确保它不会被视为作方法。
Listing 19.2 The HomeController for the default MVC app
清单 19.2 默认 MVC 应用的 HomeController
public class HomeController : Controller #A
{
private readonly ILogger<HomeController> _logger;
public HomeController(Ilogger<HomeController> logger)
{
_logger = logger;
}
public IactionResult Index() #B
{
return View(); #C
}
public IactionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, #D
NoStore = true)] #D
public IactionResult Error()
{
return View(new ErrorViewModel #E
{ #E
RequestId = Activity.Current?.Id #E
?? HttpContext.TraceIdentifier #E
}); #E
}
}
❶ MVC Controllers often inherit from the Controller base class.
MVC 控制器通常继承自 Controller 基类。
❷ Action methods are the endpoints that run in response to requests.
Action methods 是为响应请求而运行的端点。
❸ Returning View() renders a Razor view.
返回 View() 会呈现 Razor 视图。
❹ You can apply filters to actions, as you’ll learn in chapters 21 and 22.
您可以将过滤器应用于作,您将在第 21 章和第 22 章中学到。
❺ Any object returned with View is passed to the Razor view as a view model.
使用 View 返回的任何对象都将作为视图模型传递给 Razor 视图。
DEFINITION An action (or action method) is a method that runs in response to a request. An MVC controller is a class that contains one or more logically grouped action methods.
定义:一个操作 (或作方法) 是为响应请求而运行的方法。MVC 控制器是包含一个或多个逻辑分组的作方法的类。
Each of the three action methods calls View() and returns the result. This returns a ViewResult, which instructs the MVC framework to render a Razor view for the action. You’ll learn more about this process in section 19.4. The Error action method also sets an object in the call to View(). This is the view model, which is passed to the Razor view when it’s rendered.
这三个作方法中的每一个都调用 View() 并返回结果。这将返回一个 ViewResult,它指示 MVC 框架为作呈现 Razor 视图。您将在 Section 19.4 中了解有关此过程的更多信息。Error作方法还会在对 View() 的调用中设置一个对象。这是视图模型,在呈现视图时传递给 Razor 视图。
NOTE MVC controllers use explicit view models to pass data to a Razor view rather than expose the data as properties on themselves (as Razor Pages do with page models). This provides a clearer separation between the various “models” than in Razor Pages, though both Razor Pages cases use the same general MVC design pattern.
注意:MVC 控制器使用显式视图模型将数据传递到 Razor 视图,而不是将数据作为自身的属性公开(就像 Razor 页面对页面模型所做的那样)。与 Razor Pages 相比,这在各种“模型”之间提供了更清晰的分离,尽管两种 Razor Pages 情况都使用相同的通用 MVC 设计模式。
Another big difference between Razor Pages and MVC controllers is that MVC controllers typically use conventional routing, as opposed to the explicit routing used by Razor Pages. I touched on conventional routing and how it differs from explicit routing in chapter 14, but you can see it in action in this MVC application.
Razor Pages 和 MVC 控制器之间的另一个重大区别是,MVC 控制器通常使用传统路由,而不是 Razor Pages 使用的显式路由。我在第 14 章中谈到了传统路由以及它与显式路由的不同之处,但您可以在此 MVC 应用程序中看到它的实际应用。
Conventional routing defines one or more route template patterns, which are used for all the MVC controllers in your app. The default route template, shown in listing 19.1, consists of three optional segments:
传统路由定义一个或多个路由模板模式,这些模式用于应用程序中的所有 MVC 控制器。默认路由模板,如清单 19.1 所示,由三个可选段组成:
"{controller=Home}/{action=Index}/{id?}"
Conventional routes must describe which controller and action should run for any given request, so they must include controller and action route parameters at a minimum. When a request is received, ASP.NET Core matches the route template and from that calculates which MVC controller and action method to use. For example, the default route would match all the following URLs:
传统路由必须描述应该为任何给定请求运行哪个控制器和作,因此它们必须至少包含控制器和作路由参数。收到请求时,ASP.NET Core 会匹配路由模板,并从中计算要使用的 MVC 控制器和作方法。例如,默认路由将匹配以下所有 URL:
• /Home/Privacy — Executes the HomeController.Privacy() action
• /Home — Executes the HomeController.Index() action
• /customer/list — Executes the CustomerController.List() action
• /products/view/123 — Executes the ProductsController.View() action, with the route parameter id=123
With conventional routing, a single route template maps to multiple endpoints, whereas in explicit routing, one or more route templates typically map to a single endpoint. There are subtleties in both cases, but in general conventional routing is terser, and explicit routing is more expressive.
使用传统路由时,单个路由模板映射到多个终端节点,而在显式路由中,一个或多个路由模板通常映射到单个终端节点。这两种情况都有微妙之处,但通常 conventional routing 更简洁,而 explicit routing 更具表现力。
NOTE As I mentioned in chapter 14, I won’t discuss conventional routing any further in this book. It is often used only with MVC controllers, but even then, I generally prefer to use explicit routing with attributes. I describe how to use attribute routing in chapter 20 when I discuss web API controllers.
注意:正如我在第 14 章中提到的,我不会在本书中进一步讨论 conventional routing。它通常只与 MVC 控制器一起使用,但即便如此,我通常更喜欢使用带有属性的显式路由。在第 20 章中讨论 Web API 控制器时,我将介绍如何使用属性路由。
Once you’ve familiarized yourself with a basic MVC application you will likely have spotted many of the similarities and differences between the MVC framework and Razor Pages. In the next section we look at one aspect of this: MVC controllers and their Razor Page PageModel equivalent.
熟悉基本的 MVC 应用程序后,您可能已经发现了 MVC 框架和 Razor Pages 之间的许多相似之处和不同之处。在下一部分中,我们将介绍其中一个方面:MVC 控制器及其 Razor Page PageModel 等效项。
19.3 Comparing an MVC controller with a Razor Page PageModel
19.3 将 MVC 控制器与 Razor Page PageModel 进行比较
In chapter 13 we looked at the MVC design pattern, and at how it applies to Razor Pages in ASP.NET Core. Perhaps unsurprisingly, you can use MVC controllers with the MVC design pattern in almost exactly the same way.
在第 13 章中,我们了解了 MVC 设计模式,以及它如何应用于 ASP.NET Core 中的 Razor 页面。也许不足为奇的是,您可以以几乎完全相同的方式将 MVC 控制器与 MVC 设计模式一起使用。
As mentioned in section 19.2, MVC controllers and actions are analogous to their Razor Pages counterparts of PageModel and page handlers. Figure 19.3 makes this clearer; it is the MVC controller equivalent of the Razor Pages version from chapter 13.
如第 19.2 节所述,MVC 控制器和作类似于 PageModel 和页面处理程序的 Razor Pages 对应项。图 19.3 更清楚地说明了这一点;它是第 13 章中 Razor Pages 版本的 MVC 控制器等效项。
Figure 19.3 A complete MVC controller request for a category. The MVC controller pattern is almost identical to that of Razor Pages, which was shown in figure 13.12. The controller is equivalent to a Razor Page, and the action is equivalent to a page handler.
图 19.3 类别的完整 MVC 控制器请求。MVC 控制器模式与 Razor Pages 的模式几乎相同,如图 13.12 所示。控制器等效于 Razor Page,作等效于页面处理程序。
In chapter 13 I showed a simple Razor Page PageModel for displaying all the to-do items in a given category in a ToDO application. The following listing reproduces that Razor Pages code from listing 13.5 for convenience.
在第 13 章中,我展示了一个简单的 Razor Page PageModel,用于在 ToDO 应用程序中显示给定类别中的所有待办事项。为方便起见,以下清单复制了清单 13.5 中的 Razor Pages 代码。
Listing 19.3 A Razor Page for viewing all to-do items in a given category
清单 19.3 用于查看给定类别中所有待办事项的 Razor 页面
public class CategoryModel : PageModel
{
private readonly ToDoService _service;
public CategoryModel(ToDoService service)
{
_service = service;
}
public ActionResult OnGet(string category)
{
Items = _service.GetItemsForCategory(category);
return Page();
}
public List<ToDoListModel> Items { get; set; }
}
The MVC equivalent of this Razor Page is shown in listing 19.4. In the MVC framework, controllers are often used to aggregate similar actions, so the controller in this case is called ToDoController, as it would typically contain additional action methods for working with to-do items, such as actions to view a specific item or to create a new one.
此 Razor Page 的 MVC 等效项显示在清单 19.4 中。在 MVC 框架中,控制器通常用于聚合类似的作,因此在这种情况下,控制器称为 ToDoController,因为它通常包含用于处理待办事项的其他作方法,例如查看特定项或创建新项的作。
Listing 19.4 An MVC controller for viewing all to-do items in a given category
清单 19.4 一个 MVC 控制器,用于查看给定类别中的所有待办事项
public class ToDoController : Controller
{
private readonly ToDoService _service; #A
public ToDoController(ToDoService service) #A
{
_service = service;
}
public ActionResult Category(string id) #B
{
var items = _service.GetItemsForCategory(id); #C
return View(items); #D
}
public ActionResult Create(ToDoListModel model) #E
{ #E
// ... #E
} #E
}
❶ The ToDoService is provided in the controller constructor using dependency injection.
ToDoService 使用依赖项注入在控制器构造函数中提供。
❷ The Category action method takes a parameter, id.
Category作方法采用参数 id。
❸ The action method calls out to the ToDoService to retrieve data and build a view model.
方法调用 ToDoService 以检索数据并构建视图模型。
❹ Returns a ViewResult indicating the Razor view should be rendered, passing in the view model
返回一个 ViewResult,指示应呈现 Razor 视图,传入视图模型
❺ MVC controllers often contain multiple action methods that respond to different requests.
MVC 控制器通常包含多个响应不同请求的作方法。
Aside from some naming differences, the ToDoController looks similar to the Razor Page equivalent from listing 19.3:
除了一些命名差异之外,ToDoController 看起来类似于清单 19.3 中的 Razor Page 等效项:
• They both use dependency injection to access services.
它们都使用依赖关系注入来访问服务。
• Both handlers (page handler and action method) accept parameters created using model binding in exactly the same way.
两个处理程序 (页面处理程序和作方法) 都以完全相同的方式接受使用模型绑定创建的参数。
• Both interact with the application model in the same way to handle the request.
两者以相同的方式与应用程序模型交互以处理请求。
• They both create a view model for rendering the Razor view.
它们都创建用于渲染 Razor 视图的视图模型。
One of the main differences between Razor Pages and MVC controllers is in the final step: rendering the Razor view. In the next section you’ll see how to render Razor views from your MVC controller actions, how the views differ from the Razor views you’ve seen with Razor Pages, and how the framework locates the correct Razor view to render.
Razor Pages 和 MVC 控制器之间的主要区别之一是最后一步:呈现 Razor 视图。在下一部分中,你将了解如何从 MVC 控制器作呈现 Razor 视图,这些视图与使用 Razor 页面看到的 Razor 视图有何不同,以及框架如何找到要呈现的正确 Razor 视图。
19.4 Selecting a view from an MVC controller
19.4 从 MVC 控制器中选择视图
This section covers
本节涵盖
• How MVC controllers use ViewResults to render Razor views
MVC 控制器如何使用 ViewResults 呈现 Razor 视图
• How to create a new Razor view
如何创建新的 Razor 视图
• How the framework locates a Razor view to render
框架如何查找要呈现的 Razor 视图
One of the major differences between MVC controllers and Razor Pages is how the page handler or action method chooses a Razor view to render. For Razor Pages, it’s easy; the page renders the Razor view associated with the page. For MVC controllers it’s more complicated, so it’s important to understand how you choose which view to render once an action method has executed. Figure 19.4 shows a zoomed-in view of this process, right after the action has invoked the application model and received some data back.
MVC 控制器和 Razor Pages 之间的主要区别之一是页面处理程序或作方法如何选择要呈现的 Razor 视图。对于 Razor Pages,这很容易;页面呈现与页面关联的 Razor 视图。对于 MVC 控制器,情况更复杂,因此了解在执行作方法后如何选择要呈现的视图非常重要。图 19.4 显示了此过程的放大视图,该视图是在 action 调用应用程序模型并接收一些数据之后。
Figure 19.4 The process of generating HTML from an MVC controller using a ViewResult. This is similar to the process for a Razor Page. The main difference is that for Razor Pages, the view is an integral part of the Razor Page; for MVC controllers, the view must be located at runtime.
图 19.4 使用 ViewResult 从 MVC 控制器生成 HTML 的过程。这类似于 Razor 页面的过程。主要区别在于,对于 Razor Pages,视图是 Razor Page 不可或缺的一部分;对于 MVC 控制器,视图必须位于运行时。
Some of this figure should be familiar; it’s the bottom half of figure 19.3 (with a couple of additions). It shows that the MVC controller action method uses a ViewResult object to indicate that a Razor view should be rendered. This ViewResult contains the name of the Razor view template to render and a view model, an arbitrary plain old CLR object (POCO) class containing the data to render.
这个人物中的一些人应该很熟悉;它是图 19.3 的下半部分(添加了一些内容)。它显示 MVC 控制器作方法使用 ViewResult 对象来指示应呈现 Razor 视图。此 ViewResult 包含要呈现的 Razor 视图模板的名称,以及视图模型,即包含要呈现的数据的任意普通旧 CLR 对象 (POCO) 类。
NOTE ViewResult is the MVC equivalent of a Razor Page’s PageResult. The main difference is that a ViewResult includes a view name to render and a model to pass to the view template, while a PageResult always renders the Razor Page’s associated view and always passes the PageModel to the view template.
注意:ViewResult 是 Razor 页面的 PageResult 的 MVC 等效项。主要区别在于 ViewResult 包括要呈现的视图名称和要传递给视图模板的模型,而 PageResult 始终呈现 Razor Page 的关联视图,并始终将 PageModel 传递给视图模板。
After returning a ViewResult from an action method, the control flow passes back to the MVC framework, which uses a series of heuristics to locate the view, based on the template name provided. Once it locates the Razor view template, the Razor engine passes the view model from the ViewResult to the view and executes the template to generate the final HTML. This final step, rendering the HTML, is essentially the same process as for Razor Pages.
从作方法返回 ViewResult 后,控制流将传递回 MVC 框架,该框架根据提供的模板名称使用一系列启发式方法来查找视图。找到 Razor 视图模板后,Razor 引擎会将视图模型从 ViewResult 传递到视图,并执行模板以生成最终 HTML。最后一步(呈现 HTML)与 Razor Pages 的过程基本相同。
You can add a new Razor view template to your application in Visual Studio by right-clicking the folder you wish to add the view to in Solution Explorer. Choose Add > New Item
and then select Razor View - Empty from the dialog, as shown in figure 19.5. If you aren’t using Visual Studio, create a blank new file in the Views folder with the file extension .cshtml.
通过在解决方案资源管理器中右键单击要向其添加视图的文件夹,可以在 Visual Studio 中将新的 Razor 视图模板添加到应用程序中。从对话框中选择Add > New Item
并选择 Razor View - Empty,如图 19.5 所示。如果不使用 Visual Studio,请在 Views 文件夹中创建一个文件扩展名为 .cshtml 的空白新文件。
Figure 19.5 The Add New Item dialog box. Choosing Razor View - Empty adds a new Razor view template file to your application.
图 19.5 “添加新项”对话框。选择“Razor 视图 - 空”会将新的 Razor 视图模板文件添加到应用程序中。
Razor view files are almost identical to the Razor Page .cshtml files you saw in chapter 17. The only difference is that Razor view files must not specify a @page directive at the top of the file. Aside from that, they’re identical; you can use the same syntax, partial views, layouts, and view models as you can with Razor Pages. The following listing, for example, shows part of the Error.cshtml Razor view for the default MVC template. This is all recognizable as standard Razor syntax.
Razor 视图文件与你在第 17 章中看到的 Razor Page .cshtml 文件几乎相同。唯一的区别是 Razor 视图文件不得在文件顶部指定 @page 指令。除此之外,它们是相同的;您可以使用与 Razor Pages 相同的语法、分部视图、布局和视图模型。例如,以下列表显示了默认 MVC 模板的 Error.cshtml Razor 视图的一部分。这都是可识别为标准 Razor 语法的。
Listing 19.5 A Razor view
清单 19.5 Razor 视图
@model ErrorViewModel #A
@{
ViewData["Title"] = "Error"; #B
}
<h1 class="text-danger">Error.</h1> #C
<h2 class="text-danger">An error occurred while
processing your request.</h2>
@if (Model.ShowRequestId) #D
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code> #E
</p>
}
❶ Razor views may specify a view model.
Razor 视图可以指定视图模型。
❷ You can access ViewData, and execute arbitrary C# statements.
您可以访问 ViewData 并执行任意 C# 语句。
❸ Standard HTML is written directly to the output.
标准 HTML 直接写入输出。
❹ You can use standard Razor control statements and can access the view model using Model.
您可以使用标准 Razor 控制语句,并可以使用 Model 访问视图模型。
❺ You can write C# expressions using @.
您可以使用 @ 编写 C# 表达式。
With your view template created, you now need to execute it. In most cases you won’t create a ViewResult directly in your action methods. Instead, you’ll use one of the View() helper methods on the Controller base class. These helper methods simplify passing in a view model and selecting a view template, but there’s nothing magic about them; all they do is create ViewResult objects.
创建视图模板后,您现在需要执行它。在大多数情况下,您不会直接在作方法中创建 ViewResult。相反,你将在 Controller 基类上使用 View() 帮助程序方法之一。这些帮助程序方法简化了视图模型的传入和视图模板的选择,但它们并没有什么神奇之处;他们所做的只是创建 ViewResult 对象。
In the simplest case you can call the View method without any arguments, as shown in the following listing, taken from the default MVC application. The View() helper method returns a ViewResult that uses conventions to find the view template to render and does not supply a view model when executing the view.
在最简单的情况下,可以调用 View 方法,不带任何参数,如下面的清单所示,它取自默认 MVC 应用程序。帮助程序方法返回一个 ViewResult,该方法使用约定查找要呈现的视图模板,并且在执行视图时不提供视图模型。
Listing 19.6 Returning ViewResult from an action method using default conventions
示例 19.6 使用默认约定从作方法返回 ViewResult
public class HomeController : Controller #A
{
public IActionResult Index()
{
return View(); #B
}
}
In this example, the View helper method returns a ViewResult without specifying the name of the template to run. Instead, the name of the template to use is based on the name of the controller and the name of the action method. Given that the controller is called HomeController and the method is called Index, by default the Razor template engine looks for a template at the Views/Home/Index.cshtml location, as shown in figure 19.6.
在此示例中, View 帮助程序方法返回 ViewResult,而不指定要运行的模板的名称。相反,要使用的模板的名称基于控制器的名称和作方法的名称。鉴于控制器名为 HomeController 且方法名为 Index,默认情况下,Razor 模板引擎会在 Views/Home/Index.cshtml 位置查找模板,如图 19.6 所示。
Figure 19.6 View files are located at runtime based on naming conventions. Razor view files reside in a folder based on the name of the associated MVC controller and are named with the name of the action method that requested them. Views in the Shared folder can be used by any controller.
图 19.6 视图文件在运行时根据命名约定进行定位。Razor 视图文件驻留在基于关联 MVC 控制器名称的文件夹中,并使用请求它们的作方法的名称命名。Shared 文件夹中的视图可由任何控制器使用。
This is another case of using conventions in MVC to reduce the amount of boilerplate you have to write. As always, the conventions are optional. You can also explicitly pass the name of the template to run as a string to the View method. For example, if the Index method in listing 19.6 instead returned View("ListView"), the templating engine would look for a template called ListView.cshtml instead. You can even specify the complete path to the view file, relative to your application’s root folder, such as View("Views/global.cshtml"), which would look for the template at the Views/global.chtml location.
这是在 MVC 中使用约定来减少必须编写的样板数量的另一种情况。与往常一样,约定是可选的。还可以将要作为字符串运行的模板的名称显式传递给 View 方法。例如,如果列表 19.6 中的 Index 方法返回 View(“ListView”),则模板化引擎将改为查找名为 ListView.cshtml 的模板。您甚至可以指定视图文件相对于应用程序根文件夹的完整路径,例如 View(“Views/global.cshtml”),它将在 Views/global.chtml 位置查找模板。
NOTE When specifying the absolute path to a view, you must include both the top-level Views folder and the .cshtml file extension in the path. This is similar to the rules for locating partial view templates.
注意:指定视图的绝对路径时,必须在路径中同时包含顶级 Views 文件夹和 .cshtml 文件扩展名。这类似于查找局部视图模板的规则。
The process of locating an MVC Razor view is similar to the process of locating a partial view to render, which you learned about in chapter 17. The framework searches in multiple locations to find the requested view. The difference is that for Razor Pages the search process happens only for partial view rendering, as the main Razor view to render is already known; it’s the Razor Page’s view template.
查找 MVC Razor 视图的过程类似于查找要呈现的部分视图的过程,您在第 17 章中了解了该过程。框架在多个位置搜索以查找请求的视图。区别在于,对于 Razor Pages,搜索过程仅针对部分视图呈现进行,因为要呈现的主 Razor 视图是已知的;它是 Razor 页面的视图模板。
Figure 19.7 shows the complete process used by the MVC framework to locate the correct View template to execute when a ViewResult is returned from an MVC controller. It’s possible for more than one template to be eligible, such as if an Index.chstml file exists in both the Home and Shared folders. Similar to the rules for locating partial views, the engine uses the first template it finds.
图 19.7 显示了 MVC 框架用于查找从 MVC 控制器返回 ViewResult 时要执行的正确 View 模板的完整过程。多个模板可能符合条件,例如,如果 Index .chstml 文件同时存在于 Home 和 Shared 文件夹中。与查找分部视图的规则类似,引擎使用它找到的第一个模板。
Figure 19.7 A flow chart describing how the Razor templating engine locates the correct view template to execute. Avoiding the complexity of this diagram is one of the reasons I recommend using Razor Pages wherever possible!
图 19.7 描述 Razor 模板化引擎如何查找要执行的正确视图模板的流程图。避免此图表的复杂性是我建议尽可能使用 Razor Pages 的原因之一!
Tip You can modify all these conventions, including the algorithm shown in figure 19.8, during initial configuration. In fact, you can replace the whole Razor templating engine if you really want to!
提示:在初始配置期间,您可以修改所有这些约定,包括图 19.8 中所示的算法。事实上,如果您真的愿意,您可以替换整个 Razor 模板引擎!
You may find it tempting to explicitly provide the name of the view file you want to render in your controller; if so, I’d encourage you to fight that urge. You’ll have a much simpler time if you embrace the conventions as they are and go with the flow. That extends to anyone else who looks at your code; if you stick to the standard conventions, there’ll be a comforting familiarity when they look at your app. That can only be a good thing!
你可能会发现显式提供要在控制器中渲染的视图文件的名称很诱人;如果是这样,我鼓励你克制这种冲动。如果您接受惯例并顺其自然,您将度过一段轻松得多的时光。这延伸到查看你的代码的任何其他人;如果您遵守标准约定,当他们查看您的应用程序时,会有一种令人欣慰的熟悉感。那只能是一件好事!
As well as providing a view template name, you can also pass an object to act as the view model for the Razor view. This object should match the type specified in the view’s @model directive, and it’s accessed in exactly the same way as for Razor Pages; using the Model property.
除了提供视图模板名称外,您还可以传递一个对象以充当 Razor 视图的视图模型。此对象应与视图的 @model 指令中指定的类型匹配,并且访问方式与 Razor Pages 完全相同;使用 Model 属性。
Tip All the other ways of passing data to the view I described in chapter 17 are available in MVC controllers too. You should generally favor the view model where possible, but you can also use ViewData, TempData, or @inject services, for example.
提示:我在第 17 章中描述的所有其他将数据传递给视图的方法在 MVC 控制器中也可用。通常,您应该尽可能使用视图模型,但也可以使用 ViewData、TempData 或 @inject 服务等。
The following listing shows two examples of passing a view model to a view.
下面的清单显示了将视图模型传递给视图的两个示例。
Listing 19.7 Returning ViewResult from an action method using default conventions
示例 19.7 使用默认约定从作方法返回 ViewResult
public class ToDoController : Controller
{
public IActionResult Index()
{
var listViewModel = new ToDoListModel(); #A
return View(listViewModel); #B
}
public IActionResult View(int id)
{
var viewModel = new ViewToDoModel();
return View("ViewToDo", viewModel); #C
}
}
Once the Razor view template has been located, the view is rendered using the Razor syntax you learned about in chapters 17 and 18. You can use all the features you’ve already seen—layouts, partial views, _ViewImports, and _ViewStart, for example. From the point of view of the Razor view, there’s no difference between a Razor Pages view and an MVC Razor view.
找到 Razor 视图模板后,将使用您在第 17 章和第 18 章中学到的 Razor 语法呈现视图。您可以使用您已经见过的所有功能 — 例如布局、分部视图、_ViewImports 和 _ViewStart。从 Razor 视图的角度来看,Razor 页面视图和 MVC Razor 视图之间没有区别。
Now you’ve had a brief overview of an MVC application, we can look in more depth about when to choose MVC controllers over Razor Pages.
现在您已经简要概述了 MVC 应用程序,我们可以更深入地了解何时选择 MVC 控制器而不是 Razor Pages。
19.5 Choosing between Razor Pages and MVC controllers
19.5 在 Razor Pages 和 MVC 控制器之间进行选择
Throughout this book, I have said that you should generally choose Razor Pages for server-rendered applications instead of using MVC controllers. In this section I show the difference between Razor Pages and MVC controllers from a project structure point of view and defend my reasoning. I also describe the cases where MVC controllers are a good choice.
在本书中,我一直说过,您通常应该为服务器呈现的应用程序选择 Razor Pages,而不是使用 MVC 控制器。在本节中,我将从项目结构的角度展示 Razor Pages 和 MVC 控制器之间的区别,并为我的推理辩护。我还介绍了 MVC 控制器是不错选择的情况。
If you’re familiar with legacy .NET Framework ASP.NET or earlier versions of ASP.NET Core, you may already be familiar and comfortable with MVC controllers. If you’re unsure whether to stick to what you know or switch to Razor Pages, this section should help you choose. Developers coming from those backgrounds often have misconceptions about Razor Pages initially (as I did!), incorrectly equating them with Web Forms and overlooking their underlying basis of the MVC framework. This section attempts to set the record straight.
如果您熟悉旧版 .NET Framework ASP.NET 或 ASP.NET Core 的早期版本,则您可能已经熟悉并熟悉 MVC 控制器。如果您不确定是坚持您所知道的还是切换到 Razor Pages,本节应该可以帮助您进行选择。来自这些背景的开发人员最初通常对 Razor Pages 有误解(就像我一样),错误地将它们等同于 Web 窗体,而忽略了它们作为 MVC 框架的底层基础。本节试图澄清事实。
Indeed, architecturally, Razor Pages and MVC are essentially equivalent, as they both use the MVC design pattern. The most obvious differences relate to where the files are placed in your project, as I discuss in the next section.
事实上,从架构上讲,Razor Pages 和 MVC 本质上是等效的,因为它们都使用 MVC 设计模式。最明显的区别与文件在项目中的放置位置有关,我将在下一节中讨论。
19.5.1 The benefits of Razor Pages
19.5.1 Razor Pages 的优势
In section 19.5 I showed that the code for an MVC controller looks similar to the code for a Razor Page PageModel. If that’s the case, what benefit is there to using Razor Pages? In this section I discuss some of the pain points of MVC controllers and how Razor Pages attempts to address them.
在第 19.5 节中,我展示了 MVC 控制器的代码看起来类似于 Razor Page PageModel 的代码。如果是这样的话,使用 Razor Pages 有什么好处?在本节中,我将讨论 MVC 控制器的一些痛点,以及 Razor Pages 如何尝试解决这些痛点。
Razor Pages are not Web Forms
Razor Pages 不是 Web FormsA common argument I hear from existing ASP.NET developers against Razor Pages is “Oh, they’re just Web Forms.” That sentiment misses the mark in many ways, but it’s common enough that it’s worth addressing directly.
我从现有的 ASP.NET 开发人员那里听到的反对 Razor Pages 的常见论点是“哦,它们只是 Web Forms”。这种情绪在很多方面都错失了目标,但它足够普遍,值得直接解决。Web Forms was a web-programming model that was released as part of .NET Framework 1.0 in 2002. It attempted to provide a highly productive experience for developers moving from desktop development to the web for the first time.
Web Forms是一种 Web 编程模型,于 2002 年作为 .NET Framework 1.0 的一部分发布。它试图为首次从桌面开发转向 Web 的开发人员提供高效的体验。Web Forms are much maligned now, but their weaknesses only became apparent later. Web Forms attempted to hide the complexities of the web from you, to give you the impression of developing a desktop app. That often resulted in apps that were slow, with lots of interdependencies, and that were hard to maintain.
Web Forms现在受到了很多诟病,但它们的弱点后来才显现出来。Web Forms 试图向您隐藏 Web 的复杂性,让您觉得自己在开发桌面应用程序。这通常会导致应用程序运行缓慢、存在大量相互依赖关系并且难以维护。Web Forms provided a page-based programming model, which is why Razor Pages sometimes gets associated with them. However, as you’ve seen, Razor Pages is based on the MVC design pattern, and it exposes the intrinsic features of the web without trying to hide them from you.
Web Forms 提供了基于页面的编程模型,这就是 Razor Pages 有时会与它们相关联的原因。但是,正如你所看到的,Razor Pages 基于 MVC 设计模式,它公开了 Web 的内在功能,而不会试图对你隐藏它们。Razor Pages optimizes certain flows using conventions, but it’s not trying to build a stateful application model over the top of a stateless web application, in the way that Web Forms did.
Razor Pages 使用约定优化某些流,但它不会像 Web 窗体那样尝试在无状态 Web 应用程序之上构建有状态应用程序模型。If you were a fan of Web Forms’ stateful application model, you should consider Blazor Server, which uses a similar paradigm but embraces the web instead of fighting against it. You can read more about the similarities at https://learn.microsoft.com/zh-cn/dotnet/architecture/blazor-for-web-forms-developers/.
如果你是 Web Forms 的有状态应用程序模型的粉丝,你应该考虑 Blazor Server,它使用类似的范例,但拥抱 Web,而不是与之对抗。您可以在 https://learn.microsoft.com/zh-cn/dotnet/architecture/blazor-for-web-forms-developers/ 上阅读有关相似之处的更多信息。
In MVC, a single controller can have multiple action methods. Each action handles a different request and generates a different response. The grouping of multiple actions in a controller is somewhat arbitrary, but it’s typically used to group actions related to a specific entity or resource: to-do list items in this case. A more complete version of the ToDoController in listing 19.4 might include action methods for listing all to-do items, for creating new items, and for deleting items, for example. Unfortunately, you can often find that your controllers become large and bloated, with many dependencies.[1]
在 MVC 中,单个控制器可以有多个方法。每个作处理不同的请求并生成不同的响应。控制器中多个作的分组在某种程度上是任意的,但它通常用于对与特定实体或资源相关的作进行分组:在本例中为待办事项列表项。例如,清单 19.4 中更完整的 ToDoController 版本可能包括用于列出所有待办事项、创建新项和删除项的作方法。不幸的是,您经常会发现您的控制器变得庞大而臃肿,并且具有许多依赖项。[1]
NOTE You don’t have to make your controllers very large like this. It’s just a common pattern. You could, for example, create a separate controller for every action instead.
注意:您不必像这样将控制器做得非常大。这只是一种常见的模式。例如,您可以为每个作创建一个单独的控制器。
Another pitfall of MVC controllers is the way they’re typically organized in your project. Most action methods in a controller need an associated Razor view, for generating the HTML, and a view model for passing data to the view. The MVC approach in .NET traditionally groups classes by type (controller, view, view model), while the Razor Page approach groups by function; everything related to a specific page is co-located.
MVC 控制器的另一个缺陷是它们在项目中的组织方式。控制器中的大多数作方法都需要一个关联的 Razor 视图(用于生成 HTML)和一个视图模型(用于将数据传递到视图)。.NET 中的 MVC 方法传统上按类型(控制器、视图、视图模型)对类进行分组,而 Razor Page 方法按函数分组;与特定页面相关的所有内容都位于同一位置。
Figure 19.8 compares the file layout for a simple Razor Pages project with the MVC equivalent. Using Razor Pages means much less scrolling up and down between the controller, views, and view model folders whenever you’re working on a particular page. Everything you need is found in two files, the .cshtml Razor view and the (nested) .cshtml.cs PageModel file.
图 19.8 将简单 Razor Pages 项目的文件布局与 MVC 等效项进行了比较。使用 Razor Pages 意味着在处理特定页面时,在控制器、视图和视图模型文件夹之间上下滚动的时间要少得多。您需要的所有内容都可以在两个文件中找到:.cshtml Razor 视图和(嵌套的).cshtml.cs PageModel 文件。
Figure 19.8 Comparing the folder structure for an MVC project with the folder structure for a Razor Pages project
图 19.8 将 MVC 项目的文件夹结构与 Razor Pages 项目的文件夹结构进行比较
There are additional differences between MVC and Razor Pages, which I have highlighted throughout the book, but this layout difference is really the biggest win. Razor Pages embraces the fact that you’re building a page-based application and optimizes your workflow by keeping everything related to a single page together.
MVC 和 Razor Pages 之间还有其他差异,我在整本书中都强调了这些差异,但这种布局差异确实是最大的胜利。Razor Pages 接受了您正在构建基于页面的应用程序这一事实,并通过将与单个页面相关的所有内容放在一起来优化您的工作流程。
Tip You can think of each Razor Page as a mini controller focused on a single page. Page handlers are functionally equivalent to MVC controller action methods.
提示:您可以将每个 Razor 页面视为一个专注于单个页面的迷你控制器。页面处理程序在功能上等同于 MVC 控制器作方法。
This layout also has the benefit of making each page a separate class. This contrasts with the MVC approach of making each page an action on a given controller. Each Razor Page is cohesive for a particular feature, such as displaying a to-do item. MVC controllers contain action methods that handle multiple different features for a more abstract concept, such as all the features related to to-do items.
此布局还具有使每个页面成为单独类的优点。这与 MVC 方法形成鲜明对比,后者将每个页面都设置为给定控制器上的作。每个 Razor 页面对于特定功能(例如显示待办事项)都是内聚的。MVC 控制器包含作方法,这些方法处理多个不同功能,以实现更抽象的概念,例如与待办事项相关的所有功能。
NOTE ASP.NET Core is eminently customizable, so you don’t have to group your MVC applications by type; it’s simply the default state and the easy path. In fact, if you do choose to use MVC controllers, I strongly suggest grouping using feature folders instead. This MSDN article provides a good introduction: http://mng.bz/mVOr.
注意: ASP.NET Core 是高度可定制的,因此您不必按类型对 MVC 应用程序进行分组;它只是默认状态和简单的路径。事实上,如果您确实选择使用 MVC 控制器,我强烈建议改用功能文件夹进行分组。这篇 MSDN 文章提供了一个很好的介绍:http://mng.bz/mVOr。
Another important point is that Razor Pages doesn’t lose any of the separation of concerns that MVC has. The view part of Razor Pages is still concerned only with rendering HTML, and the handler is the coordinator that calls out to the application model. The only real difference is the lack of the explicit view model that you have in MVC, but it’s perfectly possible to emulate this in Razor Pages if that’s a deal-breaker for you.
另一个重要的一点是,Razor Pages 不会失去 MVC 所具有的任何关注点分离。Razor Pages 的视图部分仍然只关心呈现 HTML,处理程序是调用应用程序模型的协调器。唯一真正的区别是缺少 MVC 中的显式视图模型,但如果这对您来说是一个交易破坏者,那么在 Razor Pages 中完全可以模拟它。
The benefits of using Razor Pages are particularly noticeable when you have content websites, such as marketing websites, where you’re mostly displaying static data and there’s no real logic. In that case, MVC adds complexity without any real benefits, as there’s not really any logic in the controllers at all. Another great use case is when you’re creating forms for users to submit data. Razor Pages is especially optimized for this scenario, as you saw in previous chapters.
当你拥有内容网站(如营销网站)时,使用 Razor Pages 的好处尤其明显,因为你主要显示静态数据,没有真正的逻辑。在这种情况下,MVC 增加了复杂性,但没有任何实际的好处,因为控制器中根本没有任何真正的逻辑。另一个很好的用例是当您为用户创建表单以提交数据时。Razor Pages 特别针对此方案进行了优化,如前几章所示。
Clearly, I’m a fan of Razor Pages, but that’s not to say they’re perfect for every situation. In the next section I discuss some of the cases when you might choose to use MVC controllers in your application. Bear in mind it’s not an either-or choice; it’s possible to use MVC controllers, Razor Pages, and even minimal APIs in the same application, and in many cases that may be the best option.
显然,我是 Razor Pages 的粉丝,但这并不是说它们适合所有情况。在下一节中,我将讨论一些您可能会选择在应用程序中使用 MVC 控制器的情况。请记住,这不是一个非此即彼的选择;可以在同一应用程序中使用 MVC 控制器、Razor Pages 甚至最小的 API,在许多情况下,这可能是最佳选择。
19.5.2 When to choose MVC controllers over Razor Pages
19.5.2 何时选择 MVC 控制器而不是 Razor Pages
Razor Pages are great for building page-based server-side rendered applications. But not all applications fit that mold, and even some applications that do fall in that category might be best developed using MVC controllers instead of Razor Pages. These are a few such scenarios:
Razor Pages 非常适合构建基于页面的服务器端渲染应用程序。但并非所有应用程序都适合这种模式,甚至一些属于该类别的应用程序也可能最好使用 MVC 控制器而不是 Razor Pages 进行开发。以下是一些这样的场景:
• When you don’t want to render views—Razor Pages are best for page-based applications, where you’re rendering a view for the user. If you’re building an HTTP API, you should use minimal APIs or MVC (web API) controllers instead. You’ll learn about web API controllers in chapter 20.
当您不想呈现视图时 - Razor Pages 最适合基于页面的应用程序,您可以在其中为用户呈现视图。如果要构建 HTTP API,则应改用最少的 API 或 MVC (Web API) 控制器。您将在第 20 章中了解 Web API 控制器。
• When you’re converting an existing MVC application to ASP.NET Core—If you already have a legacy ASP.NET application that you’re converting to ASP.NET Core or an app using an early version of ASP.NET Core that you’re updating, you’re likely using MVC controllers. It’s probably not worth converting your existing MVC controllers to Razor Pages in this case. It makes more sense to keep your existing code and consider whether to do new development in the application with Razor Pages.
将现有 MVC 应用程序转换为 ASP.NET Core 时 - 如果您已经有一个要转换为 ASP.NET Core 的旧版 ASP.NET 应用程序,或者使用要更新的 ASP.NET Core 早期版本的应用程序,则您可能使用的是 MVC 控制器。在这种情况下,可能不值得将现有的 MVC 控制器转换为 Razor Pages。保留现有代码并考虑是否使用 Razor Pages 在应用程序中进行新开发更有意义。
• When you’re doing a lot of partial page updates—It’s possible to use JavaScript in an MVC application to avoid doing full page navigations by updating only part of the page at a time. This approach, halfway between fully server-side rendered and a client-side application, may be easier to achieve with MVC controllers than Razor Pages. On the other hand, you can easily mix Razor Pages and MVC controllers, using Razor Pages where appropriate and MVC controllers for the partial view results.
当您执行大量部分页面更新时 - 可以在 MVC 应用程序中使用 JavaScript,通过一次只更新页面的一部分来避免执行整个页面导航。这种方法介于完全服务器端渲染和客户端应用程序之间,使用 MVC 控制器可能比 Razor Pages 更容易实现。另一方面,您可以轻松地混合使用 Razor Pages 和 MVC 控制器,在适当的情况下使用 Razor Pages,并使用 MVC 控制器获得部分视图结果。
When not to use Razor Pages or MVC controllers
何时不使用 Razor Pages 或 MVC 控制器Typically, you’ll use either Razor Pages or MVC controllers to write most of the UI logic for an app. You’ll use it to define the APIs and pages in your application and to define how they interface with your business logic. Razor Pages and MVC provide an extensive framework and include a great deal of functionality to help build your apps quickly and efficiently. But they’re not suited to every app.
通常,你将使用 Razor Pages 或 MVC 控制器来编写应用的大部分 UI 逻辑。您将使用它来定义应用程序中的 API 和页面,并定义它们如何与您的业务逻辑交互。Razor Pages 和 MVC 提供了一个广泛的框架,并包含大量功能来帮助快速有效地构建应用。但它们并不适合每个应用程序。Providing so much functionality necessarily comes with a certain degree of performance overhead. For typical line-of-business apps, the productivity gains from using MVC or Razor Pages often outweighs any performance effect. But if you’re building a JSON API you will likely want to consider minimal APIs for the performance improvements. For server-to-server APIs or nonbrowser clients, an alternative protocol like gRPC (https://docs.microsoft.com/aspnet/core/grpc) may be a good fit. You might also consider protocols like GraphQL, as discussed in Building Web APIs in ASP.NET Core, by Valerio De Sanctis (Manning, 2023).
提供如此多的功能必然会带来一定程度的性能开销。对于典型的业务线应用,使用 MVC 或 Razor Pages 带来的工作效率提升通常超过任何性能影响。但是,如果您正在构建 JSON API,则可能需要考虑使用最少的 API 来提高性能。对于服务器到服务器 API 或非浏览器客户端,gRPC (https://docs.microsoft.com/aspnet/core/grpc) 等替代协议可能很合适。您还可以考虑像 GraphQL 这样的协议,如 Valerio De Sanctis 在 ASP.NET Core 中构建 Web API 中所述(Manning,2023 年)。Alternatively, if you’re building an app with real-time functionality, you’ll probably want to consider using WebSockets instead of traditional HTTP requests. ASP.NET Core SignalR can be used to add real-time functionality to your app by providing an abstraction over WebSockets. SignalR also provides simple transport fallbacks and a remote procedure call (RPC) app model. For details, see the documentation at https://docs.microsoft.com/aspnet/core/signalr.
或者,如果您正在构建具有实时功能的应用程序,则可能需要考虑使用 WebSockets 而不是传统的 HTTP 请求。ASP.NET Core SignalR 可用于通过通过 WebSockets 提供抽象来向应用添加实时功能。SignalR 还提供简单的传输回退和远程过程调用 (RPC) 应用程序模型。有关详细信息,请参阅 https://docs.microsoft.com/aspnet/core/signalr 中的文档。Another option available in ASP.NET Core 7 is Blazor. This framework allows you to build interactive client-side web applications by using the WebAssembly standard to run .NET code directly in your browser or by using a stateful model with SignalR. See Blazor in Action, by Chris Sainty (Manning, 2022), for more details.
ASP.NET Core 7 中提供的另一个选项是 Blazor。此框架允许您通过使用 WebAssembly 标准直接在浏览器中运行 .NET 代码,或者将有状态模型与 SignalR 结合使用来构建交互式客户端 Web 应用程序。有关更多详细信息,请参阅 Chris Sainty 的 Blazor in Action(Manning, 2022)。
I hope that by this point you’re sold on Razor Pages and their overall design using the MVC pattern. Nevertheless, using MVC controllers makes sense in some situations, so it’s worth bearing that in mind. Another important point to remember is that you can include both MVC controllers and Razor Pages in the same application if you need them.
我希望到此时,您已经对使用 MVC 模式的 Razor Pages 及其整体设计感到满意。尽管如此,在某些情况下使用 MVC 控制器是有意义的,因此值得牢记这一点。要记住的另一个重要点是,如果需要,您可以将 MVC 控制器和 Razor Pages 包含在同一个应用程序中。
You’ve learned about MVC controllers as an alternative to Razor Pages, and in part 1 of this book you learned about using minimal APIs to build JSON API. Web API controllers sit somewhere in between; they use MVC controllers but generate JSON and other machine-friendly format data, not HTML. In chapter 20 you’ll learn why you might choose to use web API controllers over minimal APIs and how to build a web API application.
您已经了解了 MVC 控制器作为 Razor Pages 的替代方案,在本书的第 1 部分中,您了解了如何使用最少的 API 来构建 JSON API。Web API 控制器介于两者之间;它们使用 MVC 控制器,但生成 JSON 和其他机器友好的格式数据,而不是 HTML。在第 20 章中,您将了解为什么您可能会选择使用 Web API 控制器而不是最少的 API,以及如何构建 Web API 应用程序。
19.6 Summary
19.6 总结
An action (or action method) is a method that runs in response to a request. An MVC controller is a class that contains one or more logically grouped action methods.
action (或作方法) 是为响应请求而运行的方法。MVC 控制器是包含一个或多个逻辑分组的作方法的类。
To use MVC controllers in an ASP.NET Core application, call AddControllersWithViews() on your WebApplicationBuilder. This adds all the required services for MVC controllers and Razor view rendering to the dependency injection container.
要在 ASP.NET Core 应用程序中使用 MVC 控制器,请在 WebApplicationBuilder 上调用 AddControllersWithViews()。这会将 MVC 控制器和 Razor 视图呈现所需的所有服务添加到依赖项注入容器中。
MVC controllers typically use conventional routing to select an MVC controller and action method. Instead of associating a route template with each action method in your application, conventional routing specifies one or more route template patterns that map to multiple endpoints. Conventional routes must define a controller and action route parameter to determine the action to execute.
MVC 控制器通常使用传统路由来选择 MVC 控制器和作方法。传统路由不是将路由模板与应用程序中的每个作方法相关联,而是指定一个或多个映射到多个终端节点的路由模板模式。传统路由必须定义控制器和作路由参数,以确定要执行的作。
You can return IActionResult instances from MVC controllers and they are executed in the same way as for Razor Pages. The most commonly returned type is ViewResult, using the View() helper method, which instructs the framework to render a Razor view.
可以从 MVC 控制器返回 IActionResult 实例,这些实例的执行方式与 Razor Pages 相同。最常返回的类型是 ViewResult,它使用 View() 帮助程序方法,该方法指示框架呈现 Razor 视图。
ViewResult may contain the name of the view to render and optionally a view model object to use when rendering the view. If the view name is not provided, a view is chosen using conventions.
ViewResult 可能包含要渲染的视图的名称,以及渲染视图时要使用的视图模型对象(可选)。如果未提供视图名称,则使用约定选择视图。
By convention, MVC Razor views are named the same as the action method that invokes them. They reside either in a folder with the same name as the action method’s controller or in the Shared folder.
按照约定,MVC Razor 视图的名称与调用它们的作方法相同。它们位于与作方法的控制器同名的文件夹中,或者位于 Shared 文件夹中。
MVC controllers contain multiple action methods, typically grouped around a high-level entity or resource. In contrast, Razor Pages groups all the page handlers for a single page in one place, grouping around a page/feature instead of an entity. This gives improved developer ergonomics when working on an endpoint.
MVC 控制器包含多个作方法,通常围绕高级实体或资源进行分组。相比之下,Razor Pages 将单个页面的所有页面处理程序分组到一个位置,围绕页面/功能而不是实体进行分组。这为开发人员在端点上工作时提供了改进的人体工程学。
MVC controllers may make sense over Razor Pages if you are upgrading an application that already uses MVC controllers or if your application is using a lot of partial page updates.
如果要升级已使用 MVC 控制器的应用程序,或者应用程序正在使用大量部分页面更新,则 MVC 控制器可能对 Razor Pages 有意义。
[1] Before moving to Razor Pages, the ASP.NET Core template that includes user login functionality contained two such controllers, each containing more than 20 action methods and more than 500 lines of code!
[1] 在迁移到 Razor Pages 之前,包含用户登录功能的 ASP.NET Core 模板包含两个这样的控制器,每个控制器包含 20 多个作方法和 500 多行代码!