Skip to content

ASP.NET Core Razor Pages 入门

创建项目

sh
$ dotnet new webapp -o RazorPagesSamples
  • 默认项目结构

aps.net core razor pages project

(图片来源:自己截的)

主要项目文件夹和文件

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.cshtmlEdit.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">
...

Last updated:

Released under the MIT License.