ASP.NET Core Razor Pages 入门指南:从零构建服务端渲染页面

ASP.NET Core Razor Pages 入门指南:从零构建服务端渲染页面 1. 先搞清楚 Razor Pages 到底解决了什么问题以及它和 MVC 的区别如果你正在用 ASP.NET Core 开发网站尤其是那种以页面为中心、交互逻辑不太复杂的应用比如后台管理系统、内容展示站或者内部工具平台那么 Razor Pages 绝对值得你优先考虑。它不是一个新东西但很多人一上来就直奔 MVC结果把简单的页面逻辑写在了 Controller 里反而让项目结构变得臃肿。Razor Pages 的核心价值在于“页面即单元”。在传统的 MVC 模式里一个功能可能涉及 Controller、Model 和 View 三个文件逻辑分散。而 Razor Pages 把处理某个特定页面比如/Products/Edit的所有东西——HTML 视图Razor 页面、页面模型PageModel和处理请求的方法OnGet, OnPost——都放在了一个.cshtml文件和一个对应的.cshtml.cs文件里。这就像给每个页面分配了一个专属的“小控制器”代码的归属感更强维护起来也更直观。和 Web API 项目相比Razor Pages 天生就是为了服务端渲染 HTML 页面设计的它内置了页面模型绑定、表单处理、验证和视图生成开箱即用。而 Web API 项目更专注于提供数据端点Endpoints返回 JSON/XML 等结构化数据。当然在 ASP.NET Core 里你完全可以在一个项目中混合使用 Razor Pages 和 Web API 控制器根据场景选择最合适的工具。所以在决定用 Razor Pages 之前先问自己我的应用是不是主要由一个个具体的页面组成每个页面是否有独立的表单提交、数据展示和业务逻辑如果是那么 Razor Pages 能让你写得更快结构更清晰。如果应用的核心是提供一套纯数据接口给移动端或前端框架调用那么直接从 Web API 项目模板开始可能更直接。2. 从零开始创建和运行你的第一个 Razor Pages 项目理论说再多不如动手跑起来。我建议直接从命令行开始这样你对项目结构会有最清晰的认识。2.1 环境准备与项目创建首先确保你安装了 .NET SDK建议使用长期支持版本如 .NET 8 或 .NET 9。打开终端PowerShell, CMD, 或 Bash执行以下命令来创建一个新的 Razor Pages 项目dotnet new webapp -o MyFirstRazorApp cd MyFirstRazorApp这条命令使用webapp模板创建了一个名为MyFirstRazorApp的 Razor Pages 项目。-o参数指定了输出目录。创建完成后用你喜欢的 IDE如 Visual Studio, VS Code, Rider打开这个文件夹。我们快速浏览一下核心目录结构Pages/:这是 Razor Pages 的心脏。每个子文件夹通常对应一个路由段里面的.cshtml和.cshtml.cs文件构成一个页面。Index.cshtmlIndex.cshtml.cs: 对应网站根路径/。Privacy.cshtmlPrivacy.cshtml.cs: 对应/Privacy路径。Shared/: 存放布局页_Layout.cshtml、局部视图等共享组件。_ViewImports.cshtml: 全局导入命名空间类似 MVC 的。_ViewStart.cshtml: 指定默认布局页。wwwroot/: 静态资源CSS, JavaScript, 图片的家。appsettings.json: 应用配置文件。Program.cs: 应用的入口和服务的配置ASP.NET Core 6 使用最小托管模型没有Startup.cs了。2.2 运行并理解默认页面在项目根目录下运行dotnet run控制台会输出应用监听的地址通常是https://localhost:5001和http://localhost:5000。用浏览器打开它你会看到一个标准的 Bootstrap 风格的首页。现在打开Pages/Index.cshtml.cs文件这是Index页面的页面模型PageModelusing Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace MyFirstRazorApp.Pages; public class IndexModel : PageModel { private readonly ILoggerIndexModel _logger; public IndexModel(ILoggerIndexModel logger) { _logger logger; } public void OnGet() { // 处理 HTTP GET 请求 } }再看Pages/Index.cshtml文件这是视图page model IndexModel { ViewData[Title] Home page; } div classtext-center h1 classdisplay-4Welcome/h1 pLearn about a hrefhttps://learn.microsoft.com/aspnet/corebuilding Web apps with ASP.NET Core/a./p /div关键点解析page指令这是 Razor Page 的标识必须放在第一行注释除外。它告诉框架这个文件是一个 Razor Page而不是普通的 Razor 视图。model IndexModel指定这个页面使用的页面模型类型建立了视图和后台代码的连接。OnGet()方法当用户通过 GET 请求访问这个页面时框架会自动调用这个方法。你可以在这里初始化页面数据。同理处理表单提交会用到OnPost()方法。这个简单的流程就是 Razor Pages 的基础请求到来 - 找到对应页面 - 执行 PageModel 中的处理器方法如OnGet- 渲染关联的.cshtml视图 - 返回 HTML。3. 核心环节实战创建带表单和数据验证的页面让我们创建一个有实际功能的页面比如一个“添加产品”的页面。这会涉及到路由、表单绑定、模型验证和处理器方法。3.1 创建页面和定义模型首先在Pages文件夹下创建一个新的子文件夹Products。然后在Products文件夹里添加一个新的 Razor Page可以右键添加也可以用命令dotnet new page -n Create -o Pages/Products。你会得到Create.cshtml和Create.cshtml.cs。我们先定义页面模型。编辑Create.cshtml.csusing System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace MyFirstRazorApp.Pages.Products; public class CreateModel : PageModel { // 这是一个绑定属性用于接收表单数据 [BindProperty] public ProductInputModel Product { get; set; } new(); // 用于在 GET 请求时展示页面 public void OnGet() { } // 用于处理表单的 POST 提交 public IActionResult OnPost() { // 检查模型状态是否有效即是否通过数据注解验证 if (!ModelState.IsValid) { // 如果验证失败返回当前页面页面上会显示验证错误信息 return Page(); } // 验证通过这里通常是保存数据到数据库的逻辑 // 例如_productService.Create(Product); _logger.LogInformation(Creating product: {Name}, Product.Name); // 重定向到其他页面比如产品列表页防止表单重复提交 return RedirectToPage(./Index); } // 内部类定义表单的数据结构 public class ProductInputModel { [Required(ErrorMessage 产品名称是必填项)] [StringLength(100, MinimumLength 3)] public string Name { get; set; } string.Empty; [Required] [DataType(DataType.Currency)] [Range(0.01, 10000)] public decimal Price { get; set; } [StringLength(500)] public string? Description { get; set; } } }关键点解析[BindProperty]这个特性至关重要。它告诉模型绑定器在 POST 请求时将表单数据绑定到Product属性上。没有它OnPost方法里的Product会是null。数据注解[Required],[StringLength]等在属性上定义验证规则。这些规则会在模型绑定后自动被框架验证结果存储在ModelState中。OnPost()方法返回值是IActionResult。ModelState.IsValid检查验证结果。如果失败return Page();会重新渲染当前页面并且因为ModelState包含了错误信息视图中的验证标签助手会自动显示这些错误。如果成功则重定向RedirectToPage这是处理 POST 请求后避免重复提交的标准做法Post-Redirect-Get 模式。3.2 构建表单视图现在编辑Create.cshtml来构建表单page model MyFirstRazorApp.Pages.Products.CreateModel { ViewData[Title] 添加产品; } h1ViewData[Title]/h1 form methodpost div classform-group label asp-forProduct.Name classcontrol-label/label input asp-forProduct.Name classform-control / span asp-validation-forProduct.Name classtext-danger/span /div div classform-group label asp-forProduct.Price classcontrol-label/label input asp-forProduct.Price classform-control / span asp-validation-forProduct.Price classtext-danger/span /div div classform-group label asp-forProduct.Description classcontrol-label/label textarea asp-forProduct.Description classform-control/textarea span asp-validation-forProduct.Description classtext-danger/span /div div classform-group mt-3 button typesubmit classbtn btn-primary提交/button a asp-page./Index classbtn btn-secondary取消/a /div /form section Scripts { {await Html.RenderPartialAsync(_ValidationScriptsPartial);} }关键点解析标签助手Tag Helpersasp-for,asp-validation-for,asp-page这些是 Razor Pages 的利器。它们在服务器端渲染成标准的 HTML但提供了强类型和智能提示。asp-forProduct.Name会自动设置input的id,name属性并与模型属性关联。asp-validation-forProduct.Name会自动显示该属性关联的验证错误信息。asp-page./Index生成指向另一个 Razor Page 的正确链接。验证脚本section Scripts部分引入了 jQuery 非侵入式验证的脚本使得客户端验证生效在输入时即时提示。这个_ValidationScriptsPartial是模板自带的。现在运行项目导航到/Products/Create尝试提交空表单或无效数据你会看到客户端和服务器端的验证都在工作。这是一个完整的、具有生产级验证功能的页面。4. 深入理解路由、处理器方法与依赖注入4.1 灵活的路由配置默认情况下页面的路由由其在Pages文件夹下的路径决定。Pages/Products/Create.cshtml对应路由/Products/Create。但你可以在page指令中自定义page /goods/new-item这样页面就通过/goods/new-item访问而不再是/Products/Create。你还可以添加路由参数page /Products/Edit/{id:int}然后在 PageModel 中接收它public void OnGet(int id) { // 根据 id 获取产品信息 }4.2 多个处理器方法一个页面不只有OnGet和OnPost。你可以有多个处理器方法来处理不同的操作。例如一个页面有两个表单public IActionResult OnPostSave() { ... } // 处理“保存”按钮 public IActionResult OnPostDelete() { ... } // 处理“删除”按钮在视图中通过表单的asp-page-handler来指定form methodpost asp-page-handlerSave form methodpost asp-page-handlerDelete4.3 依赖注入DI的使用ASP.NET Core 内置了强大的依赖注入容器。在 Razor Pages 中你可以通过构造函数注入所需服务。这在Program.cs中配置。例如假设我们有一个IProductService在Program.cs中注册服务builder.Services.AddScopedIProductService, ProductService();在 PageModel 中注入并使用public class IndexModel : PageModel { private readonly IProductService _productService; public ListProduct Products { get; set; } public IndexModel(IProductService productService) { _productService productService; } public void OnGet() { Products _productService.GetAllProducts(); } }在视图中显示foreach (var product in Model.Products) { pproduct.Name - product.Price/p }这是将业务逻辑与页面表现分离的推荐做法使 PageModel 保持精简只负责协调视图和数据。5. 常见问题排查与进阶实践要点在实际开发中你肯定会遇到一些典型问题。下面是我总结的几个高频排查点和进阶建议。5.1 常见问题排查清单页面返回 404首先检查文件位置和命名页面文件必须在Pages目录或其子目录下且包含page指令。检查路由是否在page指令中自定义了路由访问的 URL 是否匹配检查编译项目是否成功编译一个编译错误可能导致所有页面路由失效。表单提交后[BindProperty]的属性为null99% 的情况是表单字段的name属性与模型属性路径不匹配。务必使用asp-for标签助手来生成表单控件它会自动设置正确的name。手动写 HTML 很容易出错。检查OnPost方法是否被正确调用是否有同名冲突。检查模型属性是否是public且有get; set;。验证总是失败ModelState.IsValid为 false但看不出错误在OnPost方法内设置断点检查ModelState的Errors集合。里面会有具体的错误信息。常见原因客户端验证脚本未加载检查_ValidationScriptsPartial是否引入模型属性类型不匹配如向int字段输入了文本。布局Layout或样式不生效检查_ViewStart.cshtml文件是否指定了正确的布局页路径。检查静态资源CSS/JS的路径。在 Razor 页面中引用wwwroot下的资源应使用~/路径如link relstylesheet href~/css/site.css /。5.2 关于“启用远程验证Remote Validation”搜索热词中提到了“asp.net core 如何启用远程验证 remote”。这在 Razor Pages 中同样适用。远程验证允许你在用户输入时调用服务器端的一个方法来验证字段的唯一性如用户名、邮箱是否已存在。假设我们要验证产品名称是否唯一在 PageModel 中创建一个用于远程验证的 Action 方法[AcceptVerbs(GET, POST)] public IActionResult VerifyProductName(string name) { if (_productService.ProductNameExists(name)) { return Json($产品名称 {name} 已存在。); } return Json(true); }在模型属性上添加[Remote]特性[Required] [Remote(action: VerifyProductName, pageHandler: null, HttpMethod GET)] public string Name { get; set; } string.Empty;action参数指向上面那个方法名。注意远程验证需要引入 jQuery 和 jQuery 验证脚本。5.3 与 ASP.NET Core 9 及未来版本的兼容性ASP.NET Core 的更新通常非常注重向后兼容。从 .NET 8 到 .NET 9Razor Pages 的核心编程模型PageModel, Tag Helpers, 路由预计不会有颠覆性变化。主要升级可能集中在性能优化、新的 API 集成如新的 Blazor 渲染模式、以及底层 .NET 运行时的改进。我的建议是在开始一个新项目时直接使用最新的长期支持LTS版本或当前稳定版。现有项目升级时仔细阅读官方升级指南重点关注Program.cs的配置方式、中间件顺序以及任何被标记为过时Obsolete的 API。Razor Pages 本身作为一个成熟的页面模型其核心概念是稳定的。5.4 生产环境考量当你的 Razor Pages 应用要从学习走向生产时需要关注以下几点配置管理将连接字符串、API 密钥等敏感信息移出代码使用appsettings.{Environment}.json或环境变量并通过IConfiguration接口读取。日志记录充分利用 ASP.NET Core 内置的日志系统ILogger在关键位置记录信息、警告和错误。错误处理使用UseExceptionHandler中间件配置自定义错误处理页面避免向用户暴露堆栈跟踪。安全性始终使用 HTTPS。注意防范跨站请求伪造CSRF。Razor Pages 表单中form标签助手默认会生成防伪令牌input type”hidden” name”__RequestVerificationToken”在OnPost方法上通常有[ValidateAntiForgeryToken]特性默认隐式启用务必保留。对用户输入进行严格的验证和编码输出防止 XSS 攻击。性能对于复杂的数据查询考虑使用异步处理器方法OnGetAsync,OnPostAsync以避免阻塞线程。Razor Pages 提供了一条清晰、高效的路径来构建服务端渲染的 Web 应用。它的学习曲线平缓尤其适合从传统 Web Forms 过渡或希望快速构建功能页面的开发者。关键在于理解“页面即单元”的思想并熟练运用 PageModel、标签助手和模型绑定这三个核心武器。先从简单的 CRUD 页面做起逐步引入更复杂的组件和架构模式你会发现用它来组织以页面为核心的业务逻辑非常得心应手。