尧图精选

ASP.NET Core Razor Pages 从零构建CRUD网站:环境搭建到部署全流程

🕒 发布时间:2026/9/1 10:55:46 📁 来源:尧图网络
在 Web 开发领域ASP.NET Core 以其高性能、跨平台和模块化设计已成为构建现代 Web 应用的主流框架之一。对于需要快速开发、结构清晰且不涉及复杂 SPA 交互的页面型应用Razor Pages 提供了一种比传统 MVC 模型更聚焦、更直观的开发模式。它简化了控制器和视图的分离将处理特定页面的逻辑和视图紧密耦合在一个文件中使得页面职责更明确代码组织更符合直觉。本文将带你从零开始使用 ASP.NET Core Razor Pages 构建一个具备基本 CRUD 功能的网站涵盖从环境搭建、项目结构解析、页面创建、数据绑定、表单处理到部署准备的全过程。无论你是刚接触 ASP.NET Core 的开发者还是希望从 MVC 转向更轻量级页面模型的开发者都能通过本文获得一个可运行、可扩展的实践起点。1. 理解 ASP.NET Core Razor Pages 的核心设计在深入代码之前理解 Razor Pages 的设计哲学至关重要。它并非 MVC 的替代品而是在 MVC 框架之上针对“页面”这一特定场景的优化抽象。1.1 与 MVC 模式的对比传统的 ASP.NET Core MVC 遵循“模型-视图-控制器”模式一个用户请求通常需要经过路由匹配到控制器Controller控制器中的动作方法Action处理业务逻辑并选择视图View进行渲染。这种分离在大型、复杂应用中有利于职责划分但对于一个简单的“关于我们”或“联系我们”页面创建独立的控制器和视图文件可能显得繁琐。Razor Pages 将关注点从“控制器动作”转移到“页面”本身。每个 Razor 页面.cshtml文件通常对应一个物理文件路径并且拥有一个与之关联的 Page Model 类.cshtml.cs文件。这个 Page Model 类包含了处理该页面 HTTP 请求如 GET, POST的处理程序方法以及页面所需的数据和逻辑。这种设计使得与特定页面相关的所有代码都集中在一起降低了认知负担尤其适合内容导向或表单提交类的页面。1.2 Razor Pages 的核心组件一个典型的 Razor Page 项目包含以下核心部分页面文件.cshtml使用 Razor 语法混合 HTML 和 C# 代码的视图模板。它默认继承自对应的 Page Model 类。Page Model 类.cshtml.cs页面的后台代码类继承自PageModel。它包含属性用于在页面和模型之间双向绑定的数据。处理程序方法如OnGet()、OnPost()、OnPostAsync()等响应特定的 HTTP 动词。辅助方法页面相关的业务逻辑。page指令位于.cshtml文件顶部的指令如page它是将文件标记为 Razor Page 的关键并可以定义路由模板如page {id:int}。布局_Layout.cshtml和视图起始页_ViewStart.cshtml用于定义网站的统一布局和默认视图配置与 MVC 中的概念一致。依赖注入DIASP.NET Core 内置的 DI 容器可以方便地将服务如数据库上下文、日志器注入到 Page Model 的构造函数中。这种结构使得开发一个功能页面的流程变得非常线性创建页面 - 在 Page Model 中定义数据属性和处理程序 - 在 Razor 视图中绑定和渲染数据。2. 环境准备与项目创建在开始编码前需要确保开发环境就绪。我们将使用当前最新的长期支持LTS版本进行演示其稳定性和社区支持都较好。2.1 开发环境与工具.NET SDK确保安装了 .NET 8.0 SDK 或更高版本。你可以通过命令行dotnet --info来验证。集成开发环境IDEVisual Studio 2022社区版免费对 ASP.NET Core 开发提供最全面的支持包括项目模板、调试和发布工具。Visual Studio Code轻量级且跨平台需安装 C# 扩展。数据库可选为了演示数据持久化我们将使用 SQL Server LocalDBVisual Studio 自带或 SQLite。你也可以使用其他 EF Core 支持的数据库。2.2 创建新的 Razor Pages 项目使用命令行或 IDE 创建项目是最直接的方式。通过 .NET CLI 创建打开终端导航到你的工作目录运行以下命令dotnet new webapp -n RazorPagesDemo -o RazorPagesDemo cd RazorPagesDemo此命令使用webapp模板创建了一个名为RazorPagesDemo的 Razor Pages 项目。-n指定项目名称。-o指定输出目录。通过 Visual Studio 创建启动 Visual Studio选择“创建新项目”。搜索并选择“ASP.NET Core Web 应用”模板点击“下一步”。输入项目名称如RazorPagesDemo和位置点击“下一步”。在“其他信息”对话框中确保“框架”选择.NET 8.0 (长期支持)“身份验证类型”选择“无”并勾选“不使用顶级语句”可选根据编码习惯。点击“创建”。创建完成后使用dotnet run命令或在 IDE 中按 F5 运行项目。默认会在浏览器中打开https://localhost:5001或http://localhost:5000显示一个预制的欢迎页面这证明基础项目已成功运行。2.3 初始项目结构解析创建的项目包含以下关键目录和文件RazorPagesDemo/ ├── Pages/ # Razor Pages 的核心目录 │ ├── Index.cshtml # 主页对应的 Razor Page │ ├── Index.cshtml.cs # 主页的 Page Model │ ├── Privacy.cshtml # 隐私页面 │ ├── Shared/ # 共享视图组件 │ │ ├── _Layout.cshtml # 主布局文件 │ │ └── _ValidationScriptsPartial.cshtml # 客户端验证脚本 │ └── _ViewImports.cshtml # 全局导入命名空间 │ └── _ViewStart.cshtml # 设置所有页面的默认布局 ├── appsettings.json # 应用配置如连接字符串 ├── Program.cs # 应用启动和服务的配置入口 └── RazorPagesDemo.csproj # 项目文件Program.cs是应用的入口负责配置服务Service和请求处理管道Middleware Pipeline。这是理解 ASP.NET Core 启动过程的关键。3. 构建一个完整的 CRUD 功能页面我们将创建一个简单的“产品管理”页面实现产品的列表展示、创建、编辑和删除功能。这将涉及模型定义、数据访问、页面创建和表单处理。3.1 定义数据模型与数据库上下文首先在项目根目录创建一个Models文件夹并在其中添加Product.cs类代表产品实体。// Models/Product.cs namespace RazorPagesDemo.Models; public class Product { public int Id { get; set; } // 主键 public string Name { get; set; } string.Empty; public string Description { get; set; } string.Empty; public decimal Price { get; set; } public DateTime CreatedDate { get; set; } DateTime.Now; }接下来使用 Entity Framework CoreEF Core来操作数据库。首先添加必要的 NuGet 包。可以通过包管理器控制台或编辑.csproj文件实现。通过 .NET CLI 添加包dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Tools如果使用 SQLite则添加Microsoft.EntityFrameworkCore.Sqlite。然后创建数据库上下文类ApplicationDbContext.cs通常也放在Models文件夹或新建的Data文件夹中。// Data/ApplicationDbContext.cs using Microsoft.EntityFrameworkCore; using RazorPagesDemo.Models; namespace RazorPagesDemo.Data; public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptionsApplicationDbContext options) : base(options) { } public DbSetProduct Products SetProduct(); }3.2 配置依赖注入与数据库连接在Program.cs文件中我们需要注册ApplicationDbContext到依赖注入容器并配置数据库连接。// Program.cs using Microsoft.EntityFrameworkCore; using RazorPagesDemo.Data; var builder WebApplication.CreateBuilder(args); // 添加服务到容器 builder.Services.AddRazorPages(); // 配置 DbContext使用 SQL Server LocalDB builder.Services.AddDbContextApplicationDbContext(options options.UseSqlServer(builder.Configuration.GetConnectionString(DefaultConnection))); // 如果使用 SQLite // builder.Services.AddDbContextApplicationDbContext(options // options.UseSqlite(builder.Configuration.GetConnectionString(DefaultConnection))); var app builder.Build(); // ... 后续的中间件配置保持不变在appsettings.json中配置连接字符串{ ConnectionStrings: { DefaultConnection: Server(localdb)\\mssqllocaldb;DatabaseRazorPagesDemoDb;Trusted_ConnectionTrue;MultipleActiveResultSetstrue }, Logging: { LogLevel: { Default: Information, Microsoft.AspNetCore: Warning } }, AllowedHosts: * }3.3 创建 Razor Page 实现 CRUD我们将为产品管理创建一组页面。通常我们会将相关页面放在一个子文件夹下例如Pages/Products。3.3.1 创建列表/索引页Index在Pages文件夹下新建Products文件夹。右键点击Products文件夹选择“添加” - “Razor 页面”。在对话框中选择“Razor 页面 - 空”命名为Index.cshtml。这将同时生成Index.cshtml和Index.cshtml.cs。首先编辑 Page Model 文件Index.cshtml.cs// Pages/Products/Index.cshtml.cs using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; using RazorPagesDemo.Data; using RazorPagesDemo.Models; namespace RazorPagesDemo.Pages.Products; public class IndexModel : PageModel { private readonly ApplicationDbContext _context; public IndexModel(ApplicationDbContext context) { _context context; // 通过构造函数注入 DbContext } public IListProduct ProductList { get; set; } default!; // 用于在页面上显示的产品列表 public async Task OnGetAsync() // 处理页面的 GET 请求 { // 从数据库异步获取所有产品按创建日期倒序排列 ProductList await _context.Products.OrderByDescending(p p.CreatedDate).ToListAsync(); } }然后编辑 Razor 视图文件Index.cshtmlpage model RazorPagesDemo.Pages.Products.IndexModel { ViewData[Title] 产品列表; } h1产品管理/h1 p a asp-pageCreate classbtn btn-primary创建新产品/a /p table classtable thead tr thHtml.DisplayNameFor(model model.ProductList[0].Name)/th thHtml.DisplayNameFor(model model.ProductList[0].Description)/th thHtml.DisplayNameFor(model model.ProductList[0].Price)/th thHtml.DisplayNameFor(model model.ProductList[0].CreatedDate)/th th操作/th /tr /thead tbody foreach (var item in Model.ProductList) { tr tdHtml.DisplayFor(modelItem item.Name)/td tdHtml.DisplayFor(modelItem item.Description)/td tdHtml.DisplayFor(modelItem item.Price)/td tdHtml.DisplayFor(modelItem item.CreatedDate)/td td a asp-page./Edit asp-route-iditem.Id classbtn btn-sm btn-warning编辑/a a asp-page./Details asp-route-iditem.Id classbtn btn-sm btn-info详情/a a asp-page./Delete asp-route-iditem.Id classbtn btn-sm btn-danger删除/a /td /tr } /tbody /tablepage指令将其标记为 Razor Page。model指令指定了该页面使用的 Page Model 类型。asp-page和asp-route-id是 Tag Helper用于生成指向其他 Razor Page 的正确链接。Html.DisplayNameFor和Html.DisplayFor也是 Tag Helper用于安全地显示模型属性的名称和值。3.3.2 创建新增页Create同样在Pages/Products文件夹下添加一个新的 Razor 页面命名为Create.cshtml。首先编辑 Page Model 文件Create.cshtml.cs// Pages/Products/Create.cshtml.cs using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using RazorPagesDemo.Data; using RazorPagesDemo.Models; namespace RazorPagesDemo.Pages.Products; public class CreateModel : PageModel { private readonly ApplicationDbContext _context; public CreateModel(ApplicationDbContext context) { _context context; } [BindProperty] // 此属性将与表单数据进行双向绑定 public Product Product { get; set; } default!; public IActionResult OnGet() // GET 请求时仅返回页面 { return Page(); } public async TaskIActionResult OnPostAsync() // POST 请求时处理表单提交 { // 检查模型状态是否有效基于数据注解验证 if (!ModelState.IsValid) { return Page(); // 验证失败返回当前页面并显示错误信息 } // 将新产品添加到数据库 _context.Products.Add(Product); await _context.SaveChangesAsync(); // 成功后重定向到列表页 return RedirectToPage(./Index); } }然后编辑 Razor 视图文件Create.cshtmlpage model RazorPagesDemo.Pages.Products.CreateModel { ViewData[Title] 创建产品; } h1创建新产品/h1 hr / div classrow div classcol-md-4 form methodpost div asp-validation-summaryModelOnly classtext-danger/div 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.Description classcontrol-label/label textarea asp-forProduct.Description classform-control/textarea span asp-validation-forProduct.Description 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 mt-3 input typesubmit value创建 classbtn btn-primary / a asp-pageIndex classbtn btn-secondary返回列表/a /div /form /div /div section Scripts { {await Html.RenderPartialAsync(_ValidationScriptsPartial);} }form methodpost表示表单提交时将触发 Page Model 中的OnPostAsync方法。asp-forTag Helper 将 HTML 输入元素与 Page Model 中的Product属性绑定。asp-validation-for和asp-validation-summary用于显示服务端验证错误信息。section Scripts引入了客户端验证所需的 jQuery 验证脚本由_ValidationScriptsPartial提供。3.3.3 应用数据迁移并更新数据库在创建了模型和上下文后需要使用 EF Core 迁移来创建或更新数据库架构。打开终端在项目目录下或 Visual Studio 的包管理器控制台执行以下命令dotnet ef migrations add InitialCreate dotnet ef database updatemigrations add会基于你的DbContext和模型类生成迁移代码文件。database update会将迁移应用到数据库如果数据库不存在则会创建它。3.4 运行与验证现在运行应用 (dotnet run或 F5)。在浏览器中导航到/Products完整 URL 如https://localhost:5001/Products。你应该能看到产品列表页面初始为空。点击“创建新产品”填写表单并提交。成功后页面将重定向到列表页并显示新创建的产品。你可以继续测试编辑和删除功能需要按类似模式创建Edit.cshtml和Delete.cshtml页面。4. 关键机制与高级特性详解完成基础 CRUD 后需要理解背后的一些关键机制这能帮助你在遇到问题时进行排查并实现更复杂的功能。4.1 路由与page指令Razor Pages 的路由默认基于文件系统。Pages/Index.cshtml对应根路径/Pages/Products/Index.cshtml对应/ProductsPages/Products/Create.cshtml对应/Products/Create。你可以通过page指令自定义路由。例如在Pages/Products/Details.cshtml顶部添加page {id:int}这定义了一个路由模板要求 URL 中必须包含一个名为id的整数参数例如/Products/Details/5。在对应的 Page Model 中你可以添加一个名为id的参数到OnGet方法中来接收它public async TaskIActionResult OnGetAsync(int? id) { if (id null) { return NotFound(); } // ... 根据 id 查询产品 }4.2 模型绑定与验证在上面的Create页面中我们使用了[BindProperty]特性。这表示在 HTTP POST 请求中表单数据会自动绑定到Product属性上。模型绑定器会尝试将表单字段的名称如Product.Name与模型属性的名称进行匹配。数据验证通过数据注解Data Annotations实现。修改Product.cs模型类using System.ComponentModel.DataAnnotations; namespace RazorPagesDemo.Models; public class Product { public int Id { get; set; } [Required(ErrorMessage 产品名称是必填项)] [StringLength(100, ErrorMessage 名称长度不能超过100个字符)] [Display(Name 产品名称)] public string Name { get; set; } string.Empty; [Display(Name 描述)] public string Description { get; set; } string.Empty; [Required] [Range(0.01, 10000, ErrorMessage 价格必须在0.01到10000之间)] [DataType(DataType.Currency)] [Display(Name 价格)] public decimal Price { get; set; } [Display(Name 创建时间)] [DataType(DataType.DateTime)] public DateTime CreatedDate { get; set; } DateTime.Now; }添加注解后在Create或Edit页面提交表单时ModelState.IsValid会自动检查这些规则。客户端验证由_ValidationScriptsPartial引入也会在浏览器端进行即时校验。4.3 处理程序方法命名约定Razor Pages 通过处理程序方法名来响应不同的 HTTP 动词和操作。常见命名模式有OnGet/OnGetAsync响应 HTTP GET 请求用于初始化页面。OnPost/OnPostAsync响应 HTTP POST 请求通常用于处理表单提交。OnPost{HandlerName}例如OnPostDeleteAsync。当表单中有名为handler的字段input typehidden namehandler valueDelete /或其提交按钮的asp-page-handler属性设置为Delete时会调用此方法。这允许一个页面处理多个 POST 操作。4.4 部分视图与视图组件对于可重用的 UI 片段可以使用部分视图Partial View。例如创建一个_ProductSummary.cshtml放在Pages/Shared/或Pages/Products/下model Product div classcard div classcard-body h5 classcard-titleModel.Name/h5 p classcard-textModel.Description/p p classcard-textstrong价格/strongModel.Price.ToString(C)/p /div /div在Index.cshtml中可以使用以下方式渲染foreach (var item in Model.ProductList) { partial name_ProductSummary modelitem / }对于更复杂、包含逻辑的 UI 组件可以考虑使用视图组件View Component。5. 常见问题排查与调试在开发过程中你可能会遇到以下典型问题。5.1 页面 404 错误问题现象常见原因检查方式处理建议访问/Products返回 404Products文件夹下的Index.cshtml文件不存在或命名错误。检查Pages/Products/Index.cshtml文件是否存在文件名和扩展名是否正确。确保文件名和路径完全匹配。Razor Pages 默认基于文件系统路由。访问/Products/Create返回 404但文件存在。Create.cshtml顶部的page指令缺失或拼写错误。打开Create.cshtml文件检查第一行是否为page严格小写。确保每个 Razor Page 文件的第一行或紧随model之后都有page指令。路由参数不匹配如/Products/Details报错。Page Model 中的处理程序方法参数类型与page路由约束不匹配。例如路由要求{id:int}但OnGet方法参数是string id。检查page指令中的路由模板和 Page Model 方法签名。确保参数名称和类型一致。使用可空类型int?来处理可选参数。5.2 模型绑定失败或验证错误问题现象常见原因检查方式处理建议表单提交后页面刷新但数据没保存且无错误提示。Page Model 中需要绑定的属性没有添加[BindProperty]特性。检查 Page Model 中对应表单字段的属性是否用[BindProperty]修饰。为需要接收表单数据的属性添加[BindProperty]。对于 POST 专用的属性可使用[BindProperty(SupportsGet false)]。验证错误信息没有显示。1. 视图未引入验证脚本。2. 未使用asp-validation-forTag Helper。3.ModelState错误未正确添加。1. 检查视图是否包含section Scripts并渲染了_ValidationScriptsPartial。2. 检查每个输入字段后是否有对应的span asp-validation-for...。3. 在 Page Model 的 POST 方法中检查ModelState.IsValid。确保客户端和服务端验证所需的所有元素都已正确配置。使用Html.ValidationSummary()或asp-validation-summary显示全局错误。绑定到集合或复杂类型时失败。表单字段的name属性未遵循模型绑定的命名约定如Products[0].Name。查看浏览器开发者工具中网络请求的Form Data部分检查字段名。使用for循环和索引器生成表单字段或使用 Tag Helperasp-for自动生成正确的名称。5.3 数据库操作问题问题现象常见原因检查方式处理建议DbContext注入失败报空引用异常。1.ApplicationDbContext未在Program.cs中注册。2. 连接字符串配置错误或为空。1. 检查Program.cs中是否有builder.Services.AddDbContextApplicationDbContext(...)。2. 检查appsettings.json中的连接字符串名称和值。确保服务注册正确连接字符串指向有效的数据库。对于新数据库确保已执行dotnet ef database update。迁移命令失败。1. 未安装Microsoft.EntityFrameworkCore.Tools包。2. 项目文件不是启动项目。3. 存在未解决的模型编译错误。1. 运行dotnet ef看是否识别命令。2. 在解决方案中设置启动项目。3. 先编译项目确保无错误。安装必要的工具包确保项目可编译并在正确的项目目录下执行命令。5.4 调试技巧使用日志在Program.cs中配置日志级别在 Page Model 或服务中注入ILoggerT记录关键信息。浏览器开发者工具利用“网络”选项卡查看请求/响应详情、状态码和表单数据利用“控制台”查看 JavaScript 错误。设置断点在 Visual Studio 或 VS Code 中在 Page Model 的处理程序方法中设置断点逐步执行以观察变量状态和程序流程。检查中间件顺序在Program.cs的请求管道中中间件的顺序很重要如异常处理、静态文件、路由、认证授权。确保UseRouting()和UseEndpoints()的顺序正确。6. 生产环境部署与优化建议将学习项目推向生产环境需要考虑更多因素。6.1 部署准备发布项目使用dotnet publish -c Release -o ./publish命令发布项目到指定文件夹。-c Release表示使用发布配置进行优化。环境配置生产环境的连接字符串、API 密钥等敏感信息不应硬编码在appsettings.json中。应使用环境变量、Azure Key Vault 或安全的配置源。在Program.cs中环境变量会覆盖appsettings.json中的值。数据库迁移生产环境通常不会直接运行dotnet ef database update。可以考虑在 CI/CD 管道中生成 SQL 脚本 (dotnet ef migrations script)由 DBA 审核后执行。在应用启动时自动迁移需谨慎在Program.cs的app.Run()前添加using (var scope app.Services.CreateScope()) { scope.ServiceProvider.GetRequiredServiceApplicationDbContext().Database.Migrate(); }。6.2 安全加固防跨站请求伪造CSRFRazor Pages 默认启用了防伪令牌验证。确保表单中使用了Html.AntiForgeryToken()或 Form Tag Helper会自动添加令牌。在 Page Model 的 POST 方法上使用[ValidateAntiForgeryToken]特性。输入验证始终使用数据注解进行服务端验证。对于复杂逻辑可以在 Page Model 中自定义验证逻辑并手动向ModelState添加错误。SQL 注入防护使用 EF Core 的参数化查询可以有效防止 SQL 注入。绝对不要使用字符串拼接来构造 SQL 语句。HTTPS 重定向在生产环境中应强制使用 HTTPS。在Program.cs中确保app.UseHttpsRedirection()中间件被启用。6.3 性能与可维护性异步编程所有涉及 I/O 操作如数据库查询、文件读写、网络请求的方法都应使用异步模式async/await以提高应用的吞吐量和响应能力。分页查询对于列表页当数据量很大时务必实现分页。可以使用Skip()和Take()方法或 EF Core 的PaginatedList辅助类。依赖注入将业务逻辑抽象到独立的服务类中并通过构造函数注入到 Page Model。这提高了代码的可测试性和可维护性。客户端资源优化使用 Bundler Minifier 等工具压缩和合并 CSS、JavaScript 文件。考虑使用 CDN 加载常见的库如 jQuery、Bootstrap。从简单的页面构建到包含数据访问、验证和基本安全措施的 CRUD 应用Razor Pages 提供了一条清晰高效的开发路径。它的优势在于将关注点集中在“页面”这个逻辑单元上减少了 MVC 中控制器和视图之间的间接层。对于管理后台、内容展示站、内部工具等页面导向型应用这是一个非常值得考虑的架构选择。下一步你可以探索更复杂的场景如文件上传、身份认证与授权使用 ASP.NET Core Identity、实时通信SignalR或将其作为更大型应用中的一部分与 Web API 协同工作。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →