ASP.NET Core Razor Pages 实战:从零构建CRUD任务管理应用 📅 发布时间:2026/9/1 23:55:02 👁 浏览次数: 在开发Web应用时我们常常面临选择是使用传统的MVC模式还是更轻量级的方案特别是在构建以页面为中心、逻辑相对简单的后台管理系统、内容网站或内部工具时ASP.NET Core Razor Pages提供了一条清晰高效的路径。它简化了MVC中控制器Controller和视图View的分离将页面逻辑和渲染紧密耦合在同一个单元中让开发体验更直观。本文将系统性地介绍如何使用ASP.NET Core Razor Pages构建网站。无论你是刚接触.NET Web开发的新手还是希望从MVC转向更页面化开发模式的开发者都能通过本文掌握从环境搭建、基础语法、数据绑定、表单处理到部署上线的完整流程。我们将通过一个完整的“任务管理”示例项目手把手带你实现增删改查CRUD功能并深入探讨布局、部分视图、模型验证、依赖注入等核心概念最后分享生产环境的最佳实践。1. Razor Pages 核心概念与优势在深入代码之前理解Razor Pages的设计哲学及其与MVC的区别至关重要。这能帮助你在正确的场景选择正确的工具。1.1 什么是 Razor PagesRazor Pages 是 ASP.NET Core 框架中用于构建动态Web页面的一种编程模型。它基于页面Page而非控制器Controller作为组织单元。每个Razor Page通常由两个文件组成一个.cshtml文件这是Razor视图文件用于定义页面的HTML结构和UI呈现。一个.cshtml.cs文件称为“PageModel”或“代码隐藏文件”这是一个与视图同名的C#类文件用于存放该页面的处理逻辑、数据模型和事件处理方法如OnGet, OnPost。这种设计将处理特定URL请求的所有逻辑数据获取、业务处理和呈现逻辑HTML生成封装在同一个物理目录下使得功能模块的内聚性更高项目结构更清晰。1.2 与 MVC 模式的主要区别虽然Razor Pages底层依然基于MVC的架构路由、模型绑定、依赖注入等但在使用模式上有显著不同特性ASP.NET Core MVCASP.NET Core Razor Pages组织单元控制器 (Controller) 和视图 (View) 分离通常按功能组织控制器视图放在独立的Views文件夹中。页面 (Page) 作为核心单元.cshtml和.cshtml.cs文件并列存放。默认路由基于约定/{controller}/{action}/{id?}基于文件路径/Pages目录下的文件结构直接映射为URL。例如/Pages/Index.cshtml对应//Pages/Tasks/Create.cshtml对应/Tasks/Create。逻辑归属动作方法 (Action) 在控制器中定义负责处理请求并选择视图。页面处理程序 (Handler Methods) 如OnGet()、OnPost()在PageModel中定义直接与页面关联。模型传递使用ViewBag、ViewData或强类型模型从控制器传递到视图。页面模型 (PageModel) 本身就是一个强类型模型视图通过Model直接访问。适用场景大型、复杂应用API与UI混合需要高度自定义路由和架构。以页面为中心的应用程序如内容网站、后台管理、表单提交密集型应用。开发体验更简单直接。简单来说Razor Pages可以看作是MVC模式的一种简化形式它非常适合构建功能相对独立、以页面为驱动的Web应用。对于刚入门的新手它减少了在控制器和视图之间来回切换的认知负担。1.3 为什么选择 Razor Pages开发效率高页面相关的所有代码都在一起查找和修改非常方便。新建一个功能通常只需要在Pages文件夹下添加一对文件即可。易于学习和理解对于初学者或专注于前端展示的业务其基于文件系统的路由和直观的“页面处理程序”概念更容易掌握。更少的样板代码无需单独创建控制器和视图模型ViewModelPageModel集成了处理逻辑和数据模型。内置的脚手架支持Visual Studio和.NET CLI提供了强大的Razor Pages脚手架能快速生成包含CRUD操作的页面。与ASP.NET Core完全集成享受所有ASP.NET Core的特性如依赖注入、配置管理、强大的中间件管道、身份认证与授权等。2. 环境准备与项目创建工欲善其事必先利其器。让我们先搭建好开发环境。2.1 所需工具与版本操作系统Windows 10/11, macOS 或 Linux (Ubuntu等)。ASP.NET Core是跨平台的。.NET SDK本文示例基于.NET 8.0 LTS(长期支持版)。这是目前推荐用于生产环境的稳定版本。当然它也完全支持最新的.NET 9。请确保已安装。检查安装打开终端命令提示符、PowerShell或bash运行dotnet --version应显示8.0.x或9.0.x。下载地址访问 Microsoft .NET 官网 下载安装包。开发工具任选其一Visual Studio 2022 (社区版免费)功能最全的IDE对Razor Pages开发支持极佳推荐Windows用户使用。Visual Studio Code轻量级跨平台编辑器需安装C#扩展包 (ms-dotnettools.csharp)。适合所有平台。Rider (JetBrains)强大的跨平台.NET IDE。2.2 创建第一个 Razor Pages 项目我们将使用.NET CLI命令行工具创建项目这种方式通用且清晰。打开终端导航到你希望创建项目的目录。运行创建命令dotnet new webapp -n RazorPagesDemo -o RazorPagesDemodotnet new webapp使用“webapp”模板该模板默认创建的就是一个Razor Pages项目。-n RazorPagesDemo指定项目名称为RazorPagesDemo。-o RazorPagesDemo指定输出目录为同名文件夹。进入项目目录并运行cd RazorPagesDemo dotnet run查看结果命令行会显示应用正在监听某个端口如https://localhost:5001和http://localhost:5000。打开浏览器访问https://localhost:5001你将看到默认的Razor Pages欢迎页面。项目结构解析 使用VS Code或文件管理器打开项目文件夹你会看到如下关键结构RazorPagesDemo/ ├── Pages/ # Razor Pages 核心目录 │ ├── Index.cshtml # 首页对应的页面文件 │ ├── Index.cshtml.cs │ ├── Privacy.cshtml │ ├── Privacy.cshtml.cs │ ├── Shared/ # 共享的布局和组件 │ │ ├── _Layout.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ └── _ViewStart.cshtml # 设置所有页面的默认布局 ├── wwwroot/ # 静态资源文件CSS, JS, 图片 ├── appsettings.json # 应用配置文件 └── Program.cs # 应用入口和服务配置这个结构就是典型的Razor Pages项目。所有页面都位于Pages文件夹下其子目录结构直接映射为URL。3. Razor Pages 基础语法与生命周期理解PageModel和Razor语法是编写页面的基础。3.1 PageModel 详解PageModel是Razor Pages的“大脑”。打开Pages/Index.cshtml.cs文件// Pages/Index.cshtml.cs using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace RazorPagesDemo.Pages { public class IndexModel : PageModel // 继承自 PageModel { private readonly ILoggerIndexModel _logger; // 构造函数依赖注入在此进行 public IndexModel(ILoggerIndexModel logger) { _logger logger; } // 页面处理程序处理HTTP GET请求 public void OnGet() { // 当用户通过GET方式访问此页面时此方法被调用。 // 可以在这里初始化页面数据。 } // 你也可以定义处理POST请求的方法 // public IActionResult OnPost() { ... } // 定义公共属性这些属性可以在对应的 .cshtml 视图中通过 Model 访问 public string Message { get; set; } Hello from PageModel!; } }继承PageModel这是必须的。依赖注入通过构造函数注入服务如ILogger这是ASP.NET Core的核心特性。处理程序方法以On开头后跟HTTP动词Get,Post,Put,Delete等。OnGet和OnPost是最常用的。公共属性用于在页面View和PageModel之间传递数据。3.2 Razor 视图语法基础打开Pages/Index.cshtml文件查看其与PageModel的协作page !-- 这是Razor Pages指令必须放在第一行声明此文件是一个Razor Page -- model IndexModel !-- 指定此页面使用的PageModel类型这里是IndexModel -- { // C# 代码块可以在这里执行一些逻辑如设置页面标题 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 !-- 使用 Model 访问 PageModel 中的属性和方法 -- pModel.Message/p !-- 这里会输出 Hello from PageModel! -- !-- Razor 表达式 -- pCurrent time: DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss)/p !-- 控制流语句 -- { var items new Liststring { ASP.NET Core, Razor Pages, C# }; } ul foreach (var item in items) { liitem/li } /ul /div关键语法page至关重要定义此文件为Razor Page。model指定关联的PageModel提供强类型访问。{ ... }C#代码块。Model.Property访问PageModel的公共属性。DateTime.Now内联表达式输出值。foreach在HTML中混合C#控制流。3.3 页面生命周期与处理程序当一个请求到达Razor Page时大致遵循以下流程路由匹配根据URL匹配Pages文件夹下的文件路径。模型绑定如果请求带有数据如表单数据、查询字符串系统会尝试将这些数据绑定到PageModel中标记了[BindProperty]的属性上。执行处理程序根据HTTP方法GET, POST执行对应的OnGet、OnPost等方法。生成响应处理程序执行完毕后默认返回当前页面Page()。你也可以在处理方法中返回RedirectToPage()、NotFound()等其他IActionResult。渲染视图使用PageModel中的数据渲染.cshtml视图生成最终的HTML发送给客户端。命名处理程序对于同一个HTTP动词如POST你可能有多个操作。可以使用命名处理程序来区分。// 在PageModel中 public IActionResult OnPostSave() { ... } // 对应 ?handlerSave public IActionResult OnPostDelete() { ... } // 对应 ?handlerDelete在表单中可以通过asp-page-handler标签助手指定form methodpost button asp-page-handlerSave保存/button button asp-page-handlerDelete删除/button /form4. 完整实战构建一个任务管理应用现在我们综合运用所学知识构建一个具有CRUD功能的任务管理应用。我们将使用Entity Framework Core (EF Core) 作为数据访问层SQLite作为数据库便于演示。4.1 项目初始化与数据库配置添加EF Core和SQLite包。在项目根目录执行dotnet add package Microsoft.EntityFrameworkCore.Sqlite dotnet add package Microsoft.EntityFrameworkCore.Design创建数据模型。在项目根目录创建Models文件夹并添加TodoTask.cs// Models/TodoTask.cs namespace RazorPagesDemo.Models { public class TodoTask { public int Id { get; set; } // 主键 public string? Title { get; set; } public string? Description { get; set; } public bool IsCompleted { get; set; } public DateTime CreatedDate { get; set; } DateTime.Now; public DateTime? DueDate { get; set; } } }创建数据库上下文。在项目根目录创建Data文件夹并添加ApplicationDbContext.cs// Data/ApplicationDbContext.cs using Microsoft.EntityFrameworkCore; using RazorPagesDemo.Models; namespace RazorPagesDemo.Data { public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptionsApplicationDbContext options) : base(options) { } public DbSetTodoTask Tasks SetTodoTask(); // 对应数据库中的 Tasks 表 } }注册数据库上下文。修改Program.cs文件// Program.cs using Microsoft.EntityFrameworkCore; using RazorPagesDemo.Data; var builder WebApplication.CreateBuilder(args); // 添加服务到容器 builder.Services.AddRazorPages(); // 注册 ApplicationDbContext使用 SQLite数据库文件名为 app.db builder.Services.AddDbContextApplicationDbContext(options options.UseSqlite(builder.Configuration.GetConnectionString(DefaultConnection))); var app builder.Build(); // ... 后续配置不变配置连接字符串。修改appsettings.json{ Logging: { LogLevel: { Default: Information, Microsoft.AspNetCore: Warning } }, ConnectionStrings: { DefaultConnection: Data Sourceapp.db }, AllowedHosts: * }创建并应用数据库迁移。在终端执行dotnet ef migrations add InitialCreate dotnet ef database update这会在项目根目录生成Migrations文件夹并创建app.db数据库文件。4.2 实现列表页 (Index) - 读取 (R)使用脚手架生成页面。在Pages目录下创建一个Tasks子文件夹。然后使用CLI脚手架或在Visual Studio中右键添加新建脚手架项# 在 Pages/Tasks 文件夹下生成 Index, Create, Edit, Delete 等页面 dotnet new page -n Index -o Pages/Tasks dotnet new page -n Create -o Pages/Tasks dotnet new page -n Edit -o Pages/Tasks dotnet new page -n Delete -o Pages/Tasks dotnet new page -n Details -o Pages/Tasks我们手动创建Index.cshtml.cs并修改// Pages/Tasks/Index.cshtml.cs using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; using RazorPagesDemo.Data; using RazorPagesDemo.Models; namespace RazorPagesDemo.Pages.Tasks { public class IndexModel : PageModel { private readonly ApplicationDbContext _context; public IndexModel(ApplicationDbContext context) { _context context; } public IListTodoTask TodoTask { get; set; } default!; // 用于在视图中展示的任务列表 public async Task OnGetAsync() { // 从数据库异步获取所有任务按创建日期倒序排列 TodoTask await _context.Tasks .OrderByDescending(t t.CreatedDate) .ToListAsync(); } } }编写列表视图。修改Pages/Tasks/Index.cshtmlpage model RazorPagesDemo.Pages.Tasks.IndexModel { ViewData[Title] Task List; } h1Task List/h1 p a asp-pageCreate classbtn btn-primaryCreate New Task/a /p table classtable thead tr thTitle/th thDescription/th thStatus/th thDue Date/th thActions/th /tr /thead tbody foreach (var item in Model.TodoTask) { tr tdHtml.DisplayFor(modelItem item.Title)/td tdHtml.DisplayFor(modelItem item.Description)/td td if (item.IsCompleted) { span classbadge bg-successCompleted/span } else { span classbadge bg-warningPending/span } /td tdHtml.DisplayFor(modelItem item.DueDate)/td td a asp-page./Edit asp-route-iditem.Id classbtn btn-sm btn-outline-primaryEdit/a a asp-page./Details asp-route-iditem.Id classbtn btn-sm btn-outline-infoDetails/a a asp-page./Delete asp-route-iditem.Id classbtn btn-sm btn-outline-dangerDelete/a /td /tr } /tbody /tableasp-pageCreate标签助手生成指向Create页面的链接。asp-route-iditem.Id将id参数传递给目标页面。Html.DisplayFor用于安全地显示模型属性的值。4.3 实现创建页 (Create) - 新增 (C)修改 Create PageModel(Pages/Tasks/Create.cshtml.cs)using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using RazorPagesDemo.Data; using RazorPagesDemo.Models; namespace RazorPagesDemo.Pages.Tasks { public class CreateModel : PageModel { private readonly ApplicationDbContext _context; public CreateModel(ApplicationDbContext context) { _context context; } // [BindProperty] 特性使模型绑定器在POST请求时将表单数据绑定到此属性 [BindProperty] public TodoTask TodoTask { get; set; } new TodoTask(); public IActionResult OnGet() { return Page(); // 返回创建页面 } public async TaskIActionResult OnPostAsync() { // 模型状态验证 if (!ModelState.IsValid) { return Page(); // 如果验证失败返回当前页面并显示错误信息 } // 设置创建时间 TodoTask.CreatedDate DateTime.Now; // 添加到数据库上下文 _context.Tasks.Add(TodoTask); // 异步保存到数据库 await _context.SaveChangesAsync(); // 成功后重定向到列表页 return RedirectToPage(./Index); } } }编写创建视图(Pages/Tasks/Create.cshtml)page model RazorPagesDemo.Pages.Tasks.CreateModel { ViewData[Title] Create Task; } h1Create New Task/h1 hr / div classrow div classcol-md-6 form methodpost div asp-validation-summaryModelOnly classtext-danger/div !-- 显示模型级错误 -- div classform-group mb-3 label asp-forTodoTask.Title classcontrol-label/label input asp-forTodoTask.Title classform-control / span asp-validation-forTodoTask.Title classtext-danger/span !-- 显示字段级错误 -- /div div classform-group mb-3 label asp-forTodoTask.Description classcontrol-label/label textarea asp-forTodoTask.Description classform-control rows3/textarea span asp-validation-forTodoTask.Description classtext-danger/span /div div classform-group mb-3 form-check input asp-forTodoTask.IsCompleted classform-check-input / label asp-forTodoTask.IsCompleted classform-check-label/label /div div classform-group mb-3 label asp-forTodoTask.DueDate classcontrol-label/label input asp-forTodoTask.DueDate classform-control typedate / span asp-validation-forTodoTask.DueDate classtext-danger/span /div div classform-group mb-3 input typesubmit valueCreate classbtn btn-primary / a asp-pageIndex classbtn btn-secondaryBack to List/a /div /form /div /div section Scripts { {await Html.RenderPartialAsync(_ValidationScriptsPartial);} }asp-for标签助手绑定到模型属性自动生成id、name和客户端验证属性。asp-validation-summary和asp-validation-for用于显示服务端验证错误。section Scripts引入客户端验证脚本来自_ValidationScriptsPartial。4.4 实现编辑页 (Edit) - 更新 (U) 和详情页 (Details) - 读取单条编辑页 (Edit.cshtml) 和详情页 (Details.cshtml) 的实现与创建页类似核心区别在于Edit PageModelOnGet需要根据id参数从数据库查询现有记录OnPost需要更新记录。Details PageModelOnGet根据id查询记录并展示通常没有OnPost方法。这里给出Edit.cshtml.cs的关键部分// Pages/Tasks/Edit.cshtml.cs public async TaskIActionResult OnGetAsync(int? id) { if (id null) { return NotFound(); } // 根据id查找任务 var task await _context.Tasks.FirstOrDefaultAsync(m m.Id id); if (task null) { return NotFound(); } TodoTask task; // 绑定到 [BindProperty] 属性 return Page(); } public async TaskIActionResult OnPostAsync() { if (!ModelState.IsValid) { return Page(); } // 将修改后的实体标记为已修改 _context.Attach(TodoTask).State EntityState.Modified; try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!TaskExists(TodoTask.Id)) { return NotFound(); } else { throw; } } return RedirectToPage(./Index); }4.5 实现删除页 (Delete) - 删除 (D)删除操作通常需要两个步骤GET请求显示确认页面POST请求执行删除。// Pages/Tasks/Delete.cshtml.cs public async TaskIActionResult OnGetAsync(int? id) { if (id null) { return NotFound(); } // 查询要删除的任务 TodoTask await _context.Tasks.FirstOrDefaultAsync(m m.Id id); if (TodoTask null) { return NotFound(); } return Page(); // 显示确认页面 } public async TaskIActionResult OnPostAsync(int? id) { if (id null) { return NotFound(); } // 再次查询防止直接POST恶意数据 TodoTask await _context.Tasks.FindAsync(id); if (TodoTask ! null) { _context.Tasks.Remove(TodoTask); await _context.SaveChangesAsync(); } return RedirectToPage(./Index); }对应的Delete.cshtml视图主要是一个确认表单。4.6 运行与验证确保所有文件已保存。在终端运行dotnet run。访问https://localhost:5001/Tasks你应该能看到任务列表页。尝试点击“Create New Task”创建任务然后进行编辑、查看详情和删除操作。至此一个具备完整CRUD功能的Razor Pages应用就完成了。通过这个实战你应能深刻理解PageModel、Razor语法、标签助手、模型绑定和EF Core的协同工作方式。5. 进阶特性与最佳实践掌握了基础CRUD后我们来看看如何让应用更健壮、更易维护。5.1 模型验证 (Data Annotations)在TodoTask模型上添加数据注解可以实现声明式的验证规则。// Models/TodoTask.cs using System.ComponentModel.DataAnnotations; namespace RazorPagesDemo.Models { public class TodoTask { public int Id { get; set; } [Required(ErrorMessage Title is required.)] // 必填 [StringLength(100, MinimumLength 3, ErrorMessage Title must be between 3 and 100 characters.)] // 长度限制 public string? Title { get; set; } [DataType(DataType.MultilineText)] // 提示UI渲染为多行文本框 public string? Description { get; set; } [Display(Name Completed?)] // 在标签助手中显示的名称 public bool IsCompleted { get; set; } public DateTime CreatedDate { get; set; } DateTime.Now; [DataType(DataType.Date)] // 指定为日期类型 [Display(Name Due Date)] [FutureDate(ErrorMessage Due date must be a future date.)] // 自定义验证特性见下文 public DateTime? DueDate { get; set; } } }在视图中asp-validation-for标签助手会自动显示这些验证错误信息。5.2 自定义验证特性你可以创建自己的验证逻辑。例如创建一个验证截止日期必须在未来的特性// Models/FutureDateAttribute.cs using System.ComponentModel.DataAnnotations; namespace RazorPagesDemo.Models { public class FutureDateAttribute : ValidationAttribute { protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { if (value is DateTime dateValue) { if (dateValue.Date DateTime.Now.Date) { return new ValidationResult(ErrorMessage ?? Date must be in the future.); } } // 如果值为null我们认为它是有效的因为DueDate是可空的 return ValidationResult.Success; } } }然后在TodoTask.DueDate属性上应用[FutureDate]。5.3 布局 (Layout) 与部分视图 (Partial Views)布局Pages/Shared/_Layout.cshtml是所有页面的母版页。使用RenderBody()来渲染具体页面的内容。在_ViewStart.cshtml中指定默认布局。部分视图用于重用UI片段。例如创建一个_TaskTableRow.cshtml来渲染表格中的每一行!-- Pages/Tasks/_TaskTableRow.cshtml -- model RazorPagesDemo.Models.TodoTask tr tdModel.Title/td tdModel.Description/td !-- ... 其他列 ... -- /tr在Index.cshtml中使用foreach (var item in Model.TodoTask) { partial name_TaskTableRow modelitem / }5.4 依赖注入 (Dependency Injection)ASP.NET Core内置了依赖注入容器。我们已经通过构造函数注入了ApplicationDbContext和ILogger。对于业务逻辑最佳实践是创建服务接口及其实现然后在Program.cs中注册。// Services/ITaskService.cs public interface ITaskService { TaskListTodoTask GetAllTasksAsync(); TaskTodoTask? GetTaskByIdAsync(int id); // ... 其他方法 } // Services/TaskService.cs public class TaskService : ITaskService { private readonly ApplicationDbContext _context; public TaskService(ApplicationDbContext context) _context context; public async TaskListTodoTask GetAllTasksAsync() await _context.Tasks.ToListAsync(); // ... 实现其他方法 }在Program.cs中注册builder.Services.AddScopedITaskService, TaskService();在PageModel中注入并使用public class IndexModel : PageModel { private readonly ITaskService _taskService; public IndexModel(ITaskService taskService) _taskService taskService; public async Task OnGetAsync() TodoTask await _taskService.GetAllTasksAsync(); }这遵循了依赖倒置原则使代码更可测试、更松耦合。5.5 处理文件上传Razor Pages处理文件上传也很方便。需要在PageModel中定义一个IFormFile类型的属性并设置表单的enctype。// 在PageModel中 [BindProperty] public IFormFile? UploadedFile { get; set; }!-- 在视图中 -- form methodpost enctypemultipart/form-data input asp-forUploadedFile typefile classform-control / span asp-validation-forUploadedFile classtext-danger/span button typesubmitUpload/button /form在OnPost方法中可以使用UploadedFile.CopyToAsync将文件保存到服务器或云存储。6. 常见问题与排查思路在开发过程中你可能会遇到以下典型问题问题现象可能原因解决思路page指令缺失或不在首行Razor Page 未被正确识别。确保.cshtml文件第一行是page。The model item passed into the ViewDataDictionary is of type ‘X’, but this ViewDataDictionary instance requires a model item of type ‘Y’.视图顶部的model指令与PageModel类型不匹配或处理方法返回了错误的视图。检查model指令的命名空间和类名是否正确。确保OnGet/OnPost方法返回Page()或正确的IActionResult。No page named ‘/XXX’ matches the supplied values.使用asp-page或RedirectToPage时路径或参数不正确。路径是相对于/Pages文件夹的不需要扩展名。例如asp-page/Tasks/Edit。检查参数名是否匹配asp-route-{value}。模型绑定失败PageModel属性为null表单字段的name属性与[BindProperty]属性名不匹配或者未使用[BindProperty]。始终使用asp-for标签助手生成表单字段。确保POST处理方法对应的属性有[BindProperty]特性。对于复杂对象可以使用[BindProperties]作用于整个类。EF Core迁移命令失败未安装Microsoft.EntityFrameworkCore.Design包或DbContext未在Program.cs中注册。运行dotnet ef migrations add InitialCreate前确保已添加设计时包并且数据库上下文已在服务中注册AddDbContext。静态资源CSS/JS404文件未放在wwwroot目录下或路径引用错误。所有静态资源必须放在wwwroot或其子目录下。在视图中引用时路径应以~/开头例如link relstylesheet href~/css/site.css /。页面布局未生效_ViewStart.cshtml文件缺失或路径不正确或页面中显式指定了其他布局。确保Pages文件夹下有_ViewStart.cshtml文件且内容为{ Layout _Layout; }。检查页面顶部是否用{ Layout null; }覆盖了默认布局。7. 生产环境部署与优化建议当应用准备上线时需要考虑以下方面环境配置使用appsettings.Production.json或环境变量来管理生产环境的连接字符串、密钥等敏感信息。永远不要将敏感信息提交到代码仓库。数据库迁移在生产服务器上使用dotnet ef database update或在应用启动时调用context.Database.Migrate()需谨慎建议在可控的部署流程中执行迁移。使用真正的数据库将SQLite替换为更适用于生产的数据库如SQL Server、PostgreSQL或MySQL。只需更改Program.cs中的UseSqlite为UseSqlServer等并更新连接字符串。启用HTTPS重定向和HSTS在Program.cs中生产环境应确保app.UseHttpsRedirection()和app.UseHsts()被启用。静态文件缓存在Program.cs中配置静态文件中间件添加缓存头以提高性能。app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse ctx { const int durationInSeconds 60 * 60 * 24 * 30; // 30天 ctx.Context.Response.Headers[HeaderNames.CacheControl] public,max-age durationInSeconds; } });性能优化异步编程确保所有I/O操作数据库、文件、网络调用都使用异步方法async/await避免阻塞线程。分页对于数据量大的列表务必实现分页查询不要一次性加载所有数据。可以使用PaginatedListT类或第三方库。客户端资源优化捆绑Bundling和压缩MinificationCSS/JS文件。安全性防跨站请求伪造 (CSRF)Razor Pages 默认启用了防伪令牌验证。确保表单中使用了Html.AntiForgeryToken()或标签助手form methodpost会自动生成。SQL注入使用EF Core等ORM时参数化查询是默认行为能有效防止SQL注入。避免直接拼接SQL字符串。XSS防护Razor视图默认会对输出进行HTML编码使用item.Property。只有在确定内容安全时才使用Html.Raw()。日志与监控配置好日志记录如使用Serilog并集成应用性能监控APM工具以便及时发现和排查线上问题。从创建一个简单的页面到构建一个包含数据持久化、模型验证、依赖注入的完整应用Razor Pages提供了一条清晰且高效的开发路径。它完美契合了“约定大于配置”的理念让开发者能专注于业务逻辑本身。对于大多数以页面为核心的Web应用来说它是一个比标准MVC更简洁、更直接的选择。建议你以本文的示例项目为起点继续探索更高级的主题如身份认证与授权Identity、实时通信SignalR、以及构建RESTful API与Razor Pages的混合应用从而全面掌握ASP.NET Core的强大生态。