Appearance
ASP.NET Core Razor Pages 入门
创建项目
sh
$ dotnet new webapp -o RazorPagesSamples- 默认项目结构

(图片来源:自己截的)
主要项目文件夹和文件
Pages 文件夹
包含 Razor 页面和支持文件。 每个 Razor 页面都是一对文件:
- 一个
.cshtml文件,其中包含使用 Razor 语法的 C# 代码和 HTML 标签。 - 一个
.cshtml.cs文件,其中包含处理页面事件的 C# 代码。
支持文件的名称以下划线开头。 例如,_Layout.cshtml 文件可配置所有页面通用的 UI 元素。 _Layout.cshtml 设置页面顶部的导航菜单和页面底部的版权声明。
wwwroot 文件夹
包含静态资产,如 HTML 文件、JavaScript 文件和 CSS 文件。
appsettings.json
包含配置数据,如数据库连接字符串。
添加页面
- 新建Pages/HelloWorld/index.cshtml:
c#
@page
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"] - Razor Pages Samples</title>
</head>
<body>
<header>
<nav>
<!-- 导航菜单 -->
</nav>
</header>
<main>
<h2>Hello, World</h2>
<p>Hello from our View Template!</p>
</main>
<footer>
<!-- 页脚内容 -->
</footer>
</body>
</html>传递数据到页面
- 新建Pages/HelloWorld/index.cshtml.cs:
c#
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorPagesSamples.Pages.HelloWorld
{
public class IndexModel : PageModel
{
public string Message { get; set; } = default!;
public void OnGet()
{
Message = "Welcome to Razor Pages!";
}
}
}- 修改Pages/HelloWorld/index.cshtml:
c#
@page
@model RazorPagesSamples.Pages.HelloWorld.IndexModel
@{
ViewData["Title"] = "Hello, World";
}
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"] - Razor Pages Samples</title>
</head>
<body>
<header>
<nav>
<!-- 导航菜单 -->
</nav>
</header>
<main>
<h2>Hello, World</h2>
<p>Hello from our Page Template!</p>
<p>@Model.Message</p>
</main>
<footer>
<!-- 页脚内容 -->
</footer>
</body>
</html>Razor Pages 中的布局
在 ASP.NET Core Razor Pages 中,布局(Layout) 相当于传统 Web 开发中的母版页,它提供了一种在多个页面之间共享通用页面结构的机制。
按照约定,Razor Pages 中的默认布局文件为 Pages/Shared/_Layout.cshtml。
c#
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"] - Razor Pages Samples</title>
<!-- 定义可选的或必需的内容区块 -->
@RenderSection("Styles", required: false)
</head>
<body>
<header>
<nav>
<!-- 导航菜单 -->
</nav>
</header>
<main>
<!-- 标记子页面内容插入的位置 -->
@RenderBody()
</main>
<footer>
<!-- 页脚内容 -->
</footer>
<!-- 定义可选的或必需的内容区块 -->
@RenderSection("Scripts", required: false)
</body>
</html>@RenderBody()
占位符:标记子页面内容插入的位置
每个布局中必须有一个且只能有一个 @RenderBody()
@RenderSection()
定义可选的或必需的内容区块:
c#
<!-- 必需区块 -->
@RenderSection("Styles", required: true)
<!-- 可选区块 -->
@RenderSection("Scripts", required: false)页面使用布局
- 显式指定布局
c#
@{
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<!-- 或者 -->
@{
Layout = "_Layout"; // 使用页面引擎查找
}- 不使用布局
c#
@{
Layout = null;
}TIP
- 修改Pages/HelloWorld/Index.cshtml
c#
@page
@model RazorPagesSamples.Pages.HelloWorld.IndexModel
@{
Layout = "_Layout";
ViewData["Title"] = "Hello, World";
}
<h2>Hello, World</h2>
<p>Hello from our Page Template!</p>
<p>@Model.Message</p>区块(Sections)的使用
在页面中定义区块内容:
c#
@section Styles {
<style>
.custom-style { color: red; }
</style>
}
<!-- 主页面内容 -->
<div>这是页面主要内容</div>
@section Scripts {
<script>
console.log('页面加载完成');
</script>
}TIP
- 修改Pages/HelloWorld/Index.cshtml
c#
@page
@model RazorPagesSamples.Pages.HelloWorld.IndexModel
@{
Layout = "_Layout";
ViewData["Title"] = "Hello, World";
}
<h2>Hello, World</h2>
<p>Hello from our Page Template!</p>
<p>@Model.Message</p>
@section Styles {
<style>
h2 {
color: red;
}
</style>
}
@section Scripts {
<script>
alert('首页加载完成');
</script>
}在每个页面之前运行代码
需要在每个页面之前运行的代码应置于 _PageStart.cshtml 文件中。
按照约定,Razor Pages 中的 _PageStart.cshtml 文件位于 Pages/_PageStart.cshtml。常用于设置默认布局:
c#
@{
Layout = "_Layout";
}TIP
- 新建Pages/_PageStart.cshtml
c#
@{
Layout = "_Layout";
}- 修改Pages/HelloWorld/Index.cshtml
c#
@page
@model RazorPagesSamples.Pages.HelloWorld.IndexModel
@{
ViewData["Title"] = "Hello, World";
}
<h2>Hello, World</h2>
<p>Hello from our Page Template!</p>
<p>@Model.Message</p>
@section Styles {
<style>
h2 {
color: red;
}
</style>
}
@section Scripts {
<script>
alert('首页加载完成');
</script>
}为每个页面导入共享指令
页面可以使用 Razor 指令来导入命名空间并使用依赖项注入。 可在一个共同的 _ViewImports.cshtml 文件中指定由许多页面共享的指令。
按照约定,Razor Pages 中的 _ViewImports.cshtml 文件位于 Pages/_ViewImports.cshtml。
c#
@* 引入命名空间,使页面可以直接使用该命名空间下的类 *@
@using RazorPagesSamples.Pages
@using RazorPagesSamples.Models
@* 注册Tag Helper,使自定义标签助手在页面中可用 *@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers添加模型
添加数据模型类
- 添加名为 Models 的文件夹,新建Models/Product.cs:
c#
using System;
using System.ComponentModel.DataAnnotations;
namespace RazorPagesSamples.Models;
public class Product
{
public int Id { get; set; }
public required string Name { get; set; }
public string? Description { get; set; }
[DataType(DataType.Date)]
public DateTime CreatedDate { get; set; }
public decimal Price { get; set; }
}使用 aspnet-codegenerator 生成 CRUD 页面(可选)
sh
$ dotnet tool uninstall --global dotnet-aspnet-codegenerator
$ dotnet tool install --global dotnet-aspnet-codegenerator
$ dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
$ dotnet add package Microsoft.EntityFrameworkCore.Tools
$ dotnet aspnet-codegenerator razorpage -m Product -dc RazorPagesSamples.Data.ProductDbContext -udl -outDir Pages/Product --referenceScriptLibraries --databaseProvider sqlite
Building project ...
Finding the generator 'razorpage'...
Running the generator 'razorpage'...
Minimal hosting scenario!
Generating a new DbContext class 'RazorPagesSamples.Data.ProductDbContext'
Attempting to compile the application in memory with the added DbContext.
Attempting to figure out the EntityFramework metadata for the model and DbContext: 'Product'
Using database provider 'Microsoft.EntityFrameworkCore.Sqlite'!
Added DbContext : '\Data\ProductDbContext.cs'
Added Razor Page : \Pages/Product\Create.cshtml
Added PageModel : \Pages/Product\Create.cshtml.cs
Added Razor Page : \Pages/Product\Edit.cshtml
Added PageModel : \Pages/Product\Edit.cshtml.cs
Added Razor Page : \Pages/Product\Details.cshtml
Added PageModel : \Pages/Product\Details.cshtml.cs
Added Razor Page : \Pages/Product\Delete.cshtml
Added PageModel : \Pages/Product\Delete.cshtml.cs
Added Razor Page : \Pages/Product\Index.cshtml
Added PageModel : \Pages/Product\Index.cshtml.cs安装 Entity Framework Core
sh
$ dotnet add package Microsoft.EntityFrameworkCore.Sqlite- 修改Program.cs
c#
builder.Services.AddDbContext<ProductDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("ProductDbContext") ?? throw new InvalidOperationException("Connection string 'ProductDbContext' not found.")));- 修改appsettings.json
json
"ConnectionStrings": {
"ProductDbContext": "Data Source=Product.db"
}使用 dotnet-ef 生成数据库(可选)
sh
$ dotnet tool uninstall --global dotnet-ef
$ dotnet tool install --global dotnet-ef
$ dotnet add package Microsoft.EntityFrameworkCore.Design
$ dotnet ef migrations add InitialCreate
$ dotnet ef database update添加新字段
- 修改模型
在 Product.cs 中添加 Type 属性。
c#
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesSamples.Models;
public class Product
{
public int Id { get; set; }
public required string Name { get; set; }
public string? Description { get; set; }
[DataType(DataType.Date)]
public DateTime CreatedDate { get; set; }
public decimal Price { get; set; }
[DataType(DataType.Text)]
public ProductTypeEnum Type { get; set; }
}
public enum ProductTypeEnum
{
Foods, Clothes, Books
}- 修改页面
在 /Pages/Product/ 目录下的所有 .cshtml 文件中添加 Type 字段。还需要在 Create.cshtml 和 Edit.cshtml 中填充 Type 下拉列表数据。
c#
<div class="form-group">
<label asp-for="Product.Type" class="control-label"></label>
<select asp-for="Product.Type" asp-items="@Html.GetEnumSelectList<ProductTypeEnum>()">
<option value="">All</option>
</select>
<span asp-validation-for="Product.Type" class="text-danger"></span>
</div>- 使用
dotnet-ef重新生成数据库(可选)
删除 Migrations 文件夹和数据库文件,然后运行以下 .NET CLI 命令:
sh
$ dotnet ef migrations add InitialCreate
$ dotnet ef database update添加搜索
添加模糊搜索
- 在 Pages/Product/Index.cshtml.cs 中添加带有
[BindProperty(SupportsGet = true)]特性的ProductName属性:
c#
[BindProperty(SupportsGet = true)]
public string? ProductName { get; set; }- 更新 Pages/Product/Index.cshtml.cs 中的
OnGetAsync方法:
c#
public async Task OnGetAsync()
{
var products = from product in _context.Product select product;
if (!String.IsNullOrEmpty(ProductName))
{
products = products.Where(p => p.Name.Contains(ProductName));
}
Product = await products.ToListAsync();
}- 更新 Pages/Product/Index.cshtml 文件:
c#
<form method="get">
<p>
<label>Name: <input type="text" asp-for="@Model.ProductName" /></label>
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
...添加下拉列表搜索
- 在 Pages/Product/Index.cshtml.cs 中添加带有
[BindProperty(SupportsGet = true)]特性的ProductType属性:
c#
[BindProperty(SupportsGet = true)]
public string? ProductType { get; set; }- 更新 Pages/Product/Index.cshtml.cs 中的
OnGetAsync方法:
c#
public async Task OnGetAsync()
{
var products = from product in _context.Product select product;
if (!String.IsNullOrEmpty(ProductName))
{
products = products.Where(p => p.Name.Contains(ProductName));
}
if (ProductType is not null)
{
products = products.Where(p => p.Type == ProductType);
}
Product = await products.ToListAsync();
}- 更新 Pages/Product/Index.cshtml 文件:
c#
<form method="get">
<p>
<label>Name: <input type="text" asp-for="@Model.ProductName" /></label>
<label>Type: <select asp-for="@Model.ProductType" asp-items="@Html.GetEnumSelectList<ProductTypeEnum>()">
<option value="">All</option>
</select>
</label>
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
...