RazorASP.NET Core 中的頁面簡介

作者: Rick AndersonDave BrockKirk Larkin

Razor 頁面可讓以頁面為主的撰寫案例比使用控制器和檢視更容易且更具生產力。

如果您在尋找使用模型檢視控制器方法的教學課程,請參閱開始使用 ASP.NET Core MVC

本檔提供頁面簡介 Razor 。 它不是逐步教學課程。 如果您發現某些區段太進階,請參閱 開始使用 Razor 頁面。 如需 ASP.NET Core 的概觀,請參閱ASP.NET Core 簡介

必要條件

建立 Razor Pages 專案

如需如何建立 Razor Pages 專案的詳細指示,請參閱開始使用 Razor Pages

Razor 頁面

Razor 在 中 Program.cs 啟用頁面:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

在上述程式碼中:

請考慮使用基本頁面:

@page

<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>

上述程式碼看起來非常類似Razor ASP.NET Core 應用程式中使用控制器和檢視的檢視檔案。 讓它不同的是 @page 指示詞。 @page 讓檔案成為 MVC 動作,這表示它會直接處理要求,而不需要通過控制器。 @page 必須是頁面上的第一 Razor 個指示詞。 @page 會影響其他 Razor 建構的行為。 Razor 分頁檔名具有 .cshtml 尾碼。

使用PageModel類別的類似頁面,顯示於下列兩個檔案中。 Pages/Index2.cshtml 檔案:

@page
@using RazorPagesIntro.Pages
@model Index2Model

<h2>Separate page model</h2>
<p>
    @Model.Message
</p>

Pages/Index2.cshtml.cs頁面模型:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;

namespace RazorPagesIntro.Pages
{
    public class Index2Model : PageModel
    {
        public string Message { get; private set; } = "PageModel in C#";

        public void OnGet()
        {
            Message += $" Server time is { DateTime.Now }";
        }
    }
}

依照慣例,類別 PageModel 檔案的名稱與 Razor 附加的 Page 檔案 .cs 相同。 例如,上一 Razor 頁是 Pages/Index2.cshtml 。 包含 類別的 PageModel 檔案名為 Pages/Index2.cshtml.cs

頁面的 URL 路徑關聯是由頁面在檔案系統中的位置決定。 下表顯示 Razor 頁面路徑和相符的 URL:

檔案名稱和路徑 比對 URL
/Pages/Index.cshtml //Index
/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml /Store/Store/Index

注意:

  • 執行時間預設會在 Pages 資料夾中尋找 RazorPages 檔案。
  • Index 是 URL 未包含頁面時的預設頁面。

撰寫基本表單

Razor 頁面的設計目的是在建置應用程式時,輕鬆地實作與網頁瀏覽器搭配使用的常見模式。 模型系結標籤協助程式和HTML 協助程式會使用 Page 類別中 Razor 定義的屬性。 Contact 模型請考慮實作基本的「與我們連絡」格式頁面:

本文件中的範例,會在 Startup.cs 檔案中初始化 DbContext

記憶體資料庫中的 Microsoft.EntityFrameworkCore.InMemory 需要 NuGet 套件。

using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddDbContext<CustomerDbContext>(options =>
    options.UseInMemoryDatabase("name"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

資料模型:

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Models
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(10)]
        public string? Name { get; set; }
    }
}

DB 內容:

using Microsoft.EntityFrameworkCore;

namespace RazorPagesContacts.Data
{
    public class CustomerDbContext : DbContext
    {
        public CustomerDbContext (DbContextOptions<CustomerDbContext> options)
            : base(options)
        {
        }

        public DbSet<RazorPagesContacts.Models.Customer> Customer => Set<RazorPagesContacts.Models.Customer>();
    }
}

Pages/Customers/Create.cshtml 視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer!.Name" />
    <input type="submit" />
</form>

Pages/Customers/Create.cshtml.cs頁面模型:

public class CreateModel : PageModel
{
    private readonly Data.CustomerDbContext _context;

    public CreateModel(Data.CustomerDbContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    [BindProperty]
    public Customer? Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        if (Customer != null) _context.Customer.Add(Customer);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

依照慣例,PageModel 類別稱之為 <PageName>Model,與頁面位於相同的命名空間。

PageModel 類別可以分離頁面邏輯與頁面展示。 此類別會定義頁面的處理常式,以處理傳送至頁面的要求與用於轉譯頁面的資料。 此區隔允許:

POST 要求上執行的頁面具有 OnPostAsync「處理常式方法」 (當使用者張貼表單時)。 您可以新增任何 HTTP 動詞命令的處理常式方法。 最常見的處理常式包括:

  • OnGet,初始化頁所需要的狀態。 在上述程式碼中 OnGet ,方法會顯示 CreateModel.cshtmlRazor Page。
  • OnPost,處理表單提交作業。

Async 命名尾碼是選擇性的,但依照慣例通常用於非同步函式。 上述程式碼是 Razor Pages 的典型程式碼。

如果您熟悉使用控制器和檢視 ASP.NET 應用程式:

  • 上述範例中的程式 OnPostAsync 代碼看起來類似于一般控制器程式碼。
  • 大部分的 MVC 基本類型,例如 模型系結驗證和動作結果,都與 Controllers 和 Razor Pages 相同。

前一個 OnPostAsync 方法:

[BindProperty]
public Customer? Customer { get; set; }

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    if (Customer != null) _context.Customer.Add(Customer);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

OnPostAsync 的基本流程:

檢查驗證錯誤。

  • 如果沒有任何錯誤,會儲存資料並重新導向。
  • 如果有錯誤,會再次顯示有驗證訊息的頁面。 在許多情況下,會在用戶端上偵測到驗證錯誤,且永遠不會提交到伺服器。

Pages/Customers/Create.cshtml 視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer!.Name" />
    <input type="submit" />
</form>

從 轉譯的 HTML Pages/Customers/Create.cshtml

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input type="text" data-val="true"
           data-val-length="The field Name must be a string with a maximum length of 10."
           data-val-length-max="10" data-val-required="The Name field is required."
           id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
    <input type="submit" />
    <input name="__RequestVerificationToken" type="hidden"
           value="<Antiforgery token here>" />
</form>

在先前的程式碼中,張貼表單:

  • 使用有效的資料:

    • OnPostAsync處理常式方法會 RedirectToPage 呼叫協助程式方法。 RedirectToPage 會傳回 RedirectToPageResult 的執行個體。 RedirectToPage:

      • 這是動作結果。
      • 類似于 RedirectToActionRedirectToRoute (用於控制器和檢視) 。
      • 已針對頁面自訂。 在上述範例中,它會重新導向至根索引頁面 (/Index)。 產生頁面 URL一節會詳細說明 RedirectToPage
  • 傳遞至伺服器的驗證錯誤:

    • OnPostAsync處理常式方法會 Page 呼叫協助程式方法。 Page 會傳回 PageResult 的執行個體。 傳回 Page 類似於控制站中的動作傳回 ViewPageResult 是處理常式方法的預設傳回型別。 傳回 void 的處理常式方法會呈現頁面。
    • 在上述範例中,張貼沒有值的表單會導致 ModelState.IsValid 傳回 false。 在此範例中,用戶端上不會顯示任何驗證錯誤。 本檔稍後涵蓋驗證錯誤交握。
    [BindProperty]
    public Customer? Customer { get; set; }
    
    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
    
        if (Customer != null) _context.Customer.Add(Customer);
        await _context.SaveChangesAsync();
    
        return RedirectToPage("./Index");
    }
    
  • 用戶端驗證偵測到驗證錯誤:

    • 資料 不會 張貼到伺服器。
    • 本文稍後會說明用戶端驗證。

屬性 Customer 會使用 [BindProperty] 屬性加入宣告模型系結:

public class CreateModel : PageModel
{
    private readonly Data.CustomerDbContext _context;

    public CreateModel(Data.CustomerDbContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    [BindProperty]
    public Customer? Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        if (Customer != null) _context.Customer.Add(Customer);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

[BindProperty]不應該用於包含用戶端不應變更之屬性的模型。 如需詳細資訊,請參閱 Overposting

Razor 根據預設,頁面只會使用非 GET 動詞系結屬性。 系結至屬性會移除撰寫程式碼以將 HTTP 資料轉換成模型類型的需求。 透過使用相同的屬性呈現表單欄位 (<input asp-for="Customer.Name">) 並接受輸入,繫結可以減少程式碼。

警告

基於安全性考量,您必須選擇將 GET 要求資料繫結到頁面模型屬性。 請先驗證使用者輸入再將其對應至屬性。 在處理依賴查詢字串或路由值的案例時,加入宣告 GET 系結很有用。

若要在要求上 GET 系結屬性,請將 [BindProperty] 屬性的 SupportsGet 屬性設定為 true

[BindProperty(SupportsGet = true)]

如需詳細資訊,請參閱 ASP.NET Core Community Standup: Bind on GET 討論 (YouTube)

Pages/Customers/Create.cshtml 閱檢視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer!.Name" />
    <input type="submit" />
</form>
  • 在上述程式碼中,輸入標籤協助程式<input asp-for="Customer.Name" /> 會將 HTML <input> 元素系結至 Customer.Name 模型運算式。
  • @addTagHelper 讓標籤協助程式可供使用。

首頁

Index.cshtml 是首頁:

@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<h1>Contacts home page</h1>
<form method="post">
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
        @if (Model.Customers != null)
        {
            foreach (var contact in Model.Customers)
            {
                <tr>
                    <td> @contact.Id </td>
                    <td>@contact.Name</td>
                    <td>
                        <!-- <snippet_Edit> -->
                        <a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
                        <!-- </snippet_Edit> -->
                        <!-- <snippet_Delete> -->
                        <button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
                        <!-- </snippet_Delete> -->
                    </td>
                </tr>
            }
        }
        </tbody>
    </table>
    <a asp-page="Create">Create New</a>
</form>

相關聯的 PageModel 類別 (Index.cshtml.cs) :

public class IndexModel : PageModel
{
    private readonly Data.CustomerDbContext _context;
    public IndexModel(Data.CustomerDbContext context)
    {
        _context = context;
    }

    public IList<Customer>? Customers { get; set; }

    public async Task OnGetAsync()
    {
        Customers = await _context.Customer.ToListAsync();
    }

    public async Task<IActionResult> OnPostDeleteAsync(int id)
    {
        var contact = await _context.Customer.FindAsync(id);

        if (contact != null)
        {
            _context.Customer.Remove(contact);
            await _context.SaveChangesAsync();
        }

        return RedirectToPage();
    }
}

檔案 Index.cshtml 包含下列標記:

<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |

<a /a>錨點標籤協助程式使用 asp-route-{value} 屬性來產生編輯頁面的連結。 該連結包含路由資料和連絡人識別碼。 例如: https://localhost:5001/Edit/1標籤協助程式 可讓伺服器端程式碼參與在檔案中 Razor 建立和轉譯 HTML 元素。

檔案 Index.cshtml 包含標記,可為每個客戶連絡人建立刪除按鈕:

<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>

轉譯的 HTML:

<button type="submit" formaction="/Customers?id=1&amp;handler=delete">delete</button>

當刪除按鈕以 HTML 呈現時,其 formaction 會包含下列參數:

  • 由 屬性指定的 asp-route-id 客戶連絡人識別碼。
  • 屬性 handlerasp-page-handler 指定的 。

選取按鈕時,表單 POST 要求會傳送至伺服器。 依照慣例,會依據配置 OnPost[handler]Async,按 handler 參數的值來選取處理常式方法。

在此範例中,因為 handlerdelete,所以會使用 OnPostDeleteAsync 處理常式方法來處理 POST 要求。 若 asp-page-handler 設為其他值 (例如 remove),則會選取名為 OnPostRemoveAsync 的處理常式方法。

public async Task<IActionResult> OnPostDeleteAsync(int id)
{
    var contact = await _context.Customer.FindAsync(id);

    if (contact != null)
    {
        _context.Customer.Remove(contact);
        await _context.SaveChangesAsync();
    }

    return RedirectToPage();
}

OnPostDeleteAsync 方法:

  • id從查詢字串取得 。
  • 使用 FindAsync 在資料庫中查詢客戶連絡人。
  • 如果找到客戶連絡人,則會移除並更新資料庫。
  • 呼叫 RedirectToPage 以重新導向至根索引頁 (/Index)。

Edit.cshtml 檔案

@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Customer</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Customer!.Id" />
            <div class="form-group">
                <label asp-for="Customer!.Name" class="control-label"></label>
                <input asp-for="Customer!.Name" class="form-control" />
                <span asp-validation-for="Customer!.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

第一行包含 @page "{id:int}" 指示詞。 路由條件約束 "{id:int}" 會指示頁面接受包含 int 路由資料之頁面的要求。 如果頁面要求不包含可以轉換成 int 的路由資料,執行階段會傳回 HTTP 404 (找不到) 錯誤。 若要使識別碼成為選擇性,請將 ? 附加至路由條件約束:

@page "{id:int?}"

Edit.cshtml.cs 檔案:

public class EditModel : PageModel
{
    private readonly RazorPagesContacts.Data.CustomerDbContext _context;

    public EditModel(RazorPagesContacts.Data.CustomerDbContext context)
    {
        _context = context;
    }

    [BindProperty]
    public Customer? Customer { get; set; }

    public async Task<IActionResult> OnGetAsync(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        Customer = await _context.Customer.FirstOrDefaultAsync(m => m.Id == id);
        
        if (Customer == null)
        {
            return NotFound();
        }
        return Page();
    }

    // To protect from overposting attacks, enable the specific properties you want to bind to.
    // For more details, see https://aka.ms/RazorPagesCRUD.
    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        if (Customer != null)
        {
            _context.Attach(Customer).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!CustomerExists(Customer.Id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
        }

        return RedirectToPage("./Index");
    }

    private bool CustomerExists(int id)
    {
        return _context.Customer.Any(e => e.Id == id);
    }
}

驗證

驗證規則:

  • 在模型類別中以宣告方式指定。
  • 會在應用程式中的任何地方強制執行。

System.ComponentModel.DataAnnotations命名空間提供一組內建驗證屬性,這些屬性會以宣告方式套用至類別或屬性。 DataAnnotations 也包含格式化屬性 (如 [DataType]),可協助進行格式化,但不提供任何驗證。

請考慮模型 Customer

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Models
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(10)]
        public string? Name { get; set; }
    }
}

使用下列 Create.cshtml 檢視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Validation: customer name:</p>

<form method="post">
    <div asp-validation-summary="ModelOnly"></div>
    <span asp-validation-for="Customer.Name"></span>
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

上述程式碼:

  • 包含 jQuery 和 jQuery 驗證腳本。

  • <div />使用 和 <span />標籤協助程式來啟用:

    • 用戶端驗證。
    • 驗證錯誤轉譯。
  • 產生下列 HTML:

    <p>Enter a customer name:</p>
    
    <form method="post">
        Name:
        <input type="text" data-val="true"
               data-val-length="The field Name must be a string with a maximum length of 10."
               data-val-length-max="10" data-val-required="The Name field is required."
               id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
        <input type="submit" />
        <input name="__RequestVerificationToken" type="hidden"
               value="<Antiforgery token here>" />
    </form>
    
    <script src="/lib/jquery/dist/jquery.js"></script>
    <script src="/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
    

張貼沒有名稱值的 [建立表單] 會顯示表單上的錯誤訊息「需要名稱欄位」。 如果在用戶端上啟用 JavaScript,瀏覽器就會顯示錯誤,而不會張貼到伺服器。

屬性 [StringLength(10)] 會在 data-val-length-max="10" 轉譯的 HTML 上產生。 data-val-length-max 防止瀏覽器輸入超過指定的長度上限。 如果使用 Fiddler 之類的工具來編輯並重新執行文章:

  • 名稱超過 10。
  • 傳回「功能變數名稱必須是長度上限為 10 的字串」錯誤訊息。

請考慮下列 Movie 模型:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }

        [StringLength(60, MinimumLength = 3)]
        [Required]
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [Range(1, 100)]
        [DataType(DataType.Currency)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
        [Required]
        [StringLength(30)]
        public string Genre { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
        [StringLength(5)]
        [Required]
        public string Rating { get; set; }
    }
}

驗證屬性會指定要在套用至的模型屬性上強制執行的行為:

  • RequiredMinimumLength 屬性工作表示屬性必須有值,但不會防止使用者輸入空白字元來滿足此驗證。

  • RegularExpression 屬性則用來限制可輸入的字元。 在上述程式碼中,"Genre":

    • 必須指使用字母。
    • 第一個字母必須是大寫。 不允許使用空格、數字和特殊字元。
  • RegularExpression "Rating":

    • 第一個字元必須為大寫字母。
    • 允許後續空格中的特殊字元和數位。 "PG-13" 對分級而言有效,但不適用於 "Genre"。
  • Range 屬性會將值限制在指定的範圍內。

  • 屬性 StringLength 會設定字串屬性的最大長度,並選擇性地設定其最小長度。

  • 實值型別 (如decimalintfloatDateTime) 原本就是必要項目,而且不需要 [Required] 屬性。

模型的 [ Movie 建立] 頁面會顯示具有無效值的錯誤:

Movie view form with multiple jQuery client-side validation errors

如需詳細資訊,請參閱

CSS 隔離

將 CSS 樣式隔離至個別頁面、檢視和元件,以減少或避免:

  • 相依于可能難以維護的全域樣式。
  • 巢狀內容中的樣式衝突。

若要新增頁面或檢視的範圍 CSS 檔案,請將 CSS 樣式放在符合檔案名的 .cshtml 隨附 .cshtml.css 檔案中。 在下列範例中,檔案 Index.cshtml.css 會提供只套用至頁面或檢視的 Index.cshtml CSS 樣式。

Pages/Index.cshtml.cssRazor (頁面) 或 Views/Index.cshtml.css (MVC) :

h1 {
    color: red;
}

CSS 隔離會在建置階段發生。 架構會重寫 CSS 選取器,以符合應用程式頁面或檢視所呈現的標記。 重寫的 CSS 樣式會組合並產生為靜態資產 。 {APP ASSEMBLY}.styles.css 預留位置 {APP ASSEMBLY} 是專案的元件名稱。 組合 CSS 樣式的連結會放在應用程式的版面配置中。

<head> 應用程式 Pages/Shared/_Layout.cshtml (頁面的內容中, Razor) 或 Views/Shared/_Layout.cshtml (MVC) ,新增或確認連結是否存在至配套的 CSS 樣式:

<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />

在下列範例中,應用程式的元件名稱為 WebApp

<link rel="stylesheet" href="WebApp.styles.css" />

範圍 CSS 檔案中定義的樣式只會套用至相符檔案的轉譯輸出。 在上述範例中,在應用程式中其他位置定義的任何 h1 CSS 宣告不會與 Index 標題樣式衝突。 CSS 樣式串聯和繼承規則對於限定範圍的 CSS 檔案仍有效。 例如,直接套用至 <h1> 檔案中的 Index.cshtml 元素的樣式會覆寫 中範圍 CSS 檔案的 Index.cshtml.css 樣式。

注意

為了保證發生統合時的 CSS 樣式隔離,不支援在程式碼區塊中匯入 Razor CSS。

CSS 隔離僅適用于 HTML 元素。 標籤協助程式不支援 CSS 隔離。

在配套的 CSS 檔案中,每個頁面、檢視或 Razor 元件都會與格式 b-{STRING} 為 的範圍識別碼相關聯,其中 {STRING} 預留位置是架構所產生的十個字元字串。 下列範例提供 Pages 應用程式頁面中上述 <h1> 元素 Index 的 Razor 樣式:

/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
    color: red;
}

Index在從配套檔案套用 CSS 樣式的頁面中,範圍識別碼會附加為 HTML 屬性:

<h1 b-3xxtam6d07>

識別碼對應用程式而言是唯一的。 在建置階段,會使用慣例 {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css 來建立專案配套,其中預留位置 {STATIC WEB ASSETS BASE PATH} 是靜態 Web 資產基底路徑。

如果使用其他專案,例如 NuGet 套件或Razor 類別庫,組合檔案:

  • 使用 CSS 匯入來參考樣式。
  • 不會發佈為取用樣式之應用程式的靜態 Web 資產。

CSS 預處理器支援

CSS 預處理器有助於利用變數、巢狀、模組、混合和繼承等功能來改善 CSS 開發。 雖然 CSS 隔離原本不支援 Sass 或 Less 等 CSS 預處理器,但只要架構在建置程式期間重寫 CSS 選取器之前進行預處理器編譯,整合 CSS 預處理器就會順暢。 例如,使用 Visual Studio,將現有的預處理器編譯設定為 Visual Studio 工作執行器總管中的 [建置前 ] 工作。

許多協力廠商 NuGet 套件,例如 Delegate.SassBuilder ,可以在建置程式的開頭編譯 SASS/SCSS 檔案,然後再進行 CSS 隔離,而且不需要額外的設定。

CSS 隔離設定

CSS 隔離允許某些進階案例的組態,例如當現有工具或工作流程有相依性時。

自訂範圍識別碼格式

在本節中 {Pages|Views} ,預留位置適用于 PagesRazor Pages 應用程式或 Views MVC 應用程式。

根據預設,範圍識別碼會使用 格式 b-{STRING} ,其中 {STRING} 預留位置是架構所產生的十個字元字串。 若要自訂範圍識別碼格式,請將專案檔更新為所需的模式:

<ItemGroup>
  <None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

在上述範例中,針對 Index.cshtml.css 將其範圍識別碼從 b-{STRING} 變更為 所產生的 CSS。 custom-scope-identifier

使用範圍識別碼來達成範圍 CSS 檔案的繼承。 在下列專案檔範例中,檔案 BaseView.cshtml.css 包含跨檢視的通用樣式。 檔案 DerivedView.cshtml.css 會繼承這些樣式。

<ItemGroup>
  <None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
  <None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

使用萬用字元 (*) 運算子,跨多個檔案共用範圍識別碼:

<ItemGroup>
  <None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

變更靜態 Web 資產的基底路徑

範圍 CSS 檔案會在應用程式的根目錄產生。 在專案檔中 StaticWebAssetBasePath ,使用 屬性來變更預設路徑。 下列範例會將範圍 CSS 檔案和應用程式的其餘資產 _content 放在路徑上:

<PropertyGroup>
  <StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>

停用自動統合

若要退出宣告架構在執行時間發佈和載入範圍檔案的方式,請使用 DisableScopedCssBundling 屬性。 使用此屬性時,其他工具或進程會負責從 obj 目錄取得隔離的 CSS 檔案,並在執行時間發佈和載入它們:

<PropertyGroup>
  <DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>

Razor 類別庫 (RCL) 支援

Razor 當類別庫 (RCL) 提供隔離樣式時, <link> 標籤的屬性 href 會指向 {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css ,其中預留位置為:

  • {STATIC WEB ASSET BASE PATH}:靜態 Web 資產基底路徑。
  • {PACKAGE ID}:程式庫的 套件識別碼。 如果未在專案檔中指定封裝識別碼,封裝識別碼預設為專案的元件名稱。

在下例中︰

  • 靜態 Web 資產基底路徑為 _content/ClassLib
  • 類別庫的元件名稱為 ClassLib

Pages/Shared/_Layout.cshtmlRazor (頁面) 或 Views/Shared/_Layout.cshtml (MVC) :

<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">

如需 RCL 的詳細資訊,請參閱下列文章:

如需 CSS 隔離的相關資訊 Blazor ,請參閱 核心 CSS 隔離 ASP.NET Blazor

使用 OnGet 處理常式後援來處理 HEAD 要求

HEAD 要求允許擷取特定資源的標頭。 不同於 GET 要求,HEAD 要求不會傳回回應主體。

一般來說,會為 HEAD 要求建立及呼叫 OnHead 處理常式:

public void OnHead()
{
    HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}

Razor如果未 OnHead 定義處理常式, OnGet 頁面會回復為呼叫處理常式。

XSRF/CSRF 和 Razor 頁面

Razor 頁面受到 Antiforgery 驗證的保護。 FormTagHelper會將反移轉權杖插入 HTML 表單元素中。

搭配頁面使用版面配置、部分、範本和標籤協助程式 Razor

頁面會使用檢視引擎的所有功能 Razor 。 版面配置、部分、範本、標籤協助程式、 _ViewStart.cshtml_ViewImports.cshtml 的運作方式與傳統 Razor 檢視相同。

可利用這些功能的一部分來整理這個頁面。

版面配置頁面 新增至 Pages/Shared/_Layout.cshtml

<!DOCTYPE html>
<html>
<head>
    <title>RP Sample</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
    <a asp-page="/Index">Home</a>
    <a asp-page="/Customers/Create">Create</a>
    <a asp-page="/Customers/Index">Customers</a> <br />

    @RenderBody()
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>

面配置

  • 控制每個頁面的版面配置 (除非頁面退出版面配置)。
  • 匯入 HTML 結構,例如 JavaScript 和樣式表。
  • 頁面的內容 Razor 會在呼叫的位置 @RenderBody() 轉譯。

如需詳細資訊,請參閱 版面配置頁面

Layout屬性是在 中 Pages/_ViewStart.cshtml 設定:

@{
    Layout = "_Layout";
}

版面配置位於 Pages/Shared 資料夾。 頁面會以階層方式尋找其他檢視 (版面配置、範本、部分),從目前頁面的相同資料夾開始。 Pages/Shared資料夾中的版面配置可從Pages資料夾下的任何 Razor 頁面使用。

版面配置頁面應位於 Pages/Shared 資料夾中。

我們建議您將配置檔案放入 Views/Shared 資料夾。 Views/Shared 是 MVC 檢視模式。 Razor 頁面的目的是依賴資料夾階層,而不是路徑慣例。

從 Razor 頁面檢視搜尋包含 Pages 資料夾。 與 MVC 控制器和傳統 Razor 檢視搭配使用的版面配置、範本和部分 僅能運作

新增檔案 Pages/_ViewImports.cshtml

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

本教學課程稍後會說明 @namespace@addTagHelper 指示詞會將內建標記協助程式帶入 Pages 資料夾中的所有頁面。

@namespace在頁面上設定的 指示詞:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

<h2>Name space</h2>
<p>
    @Model.Message
</p>

指示詞 @namespace 會設定頁面的命名空間。 @model 指示詞不需要包含命名空間。

@namespace當 指示詞包含在 中 _ViewImports.cshtml 時,指定的命名空間會在匯入 指示詞的 Page @namespace 中提供所產生命名空間的前置詞。 產生的命名空間的其餘部分 (尾碼部分) 是包含 _ViewImports.cshtml 資料夾與包含頁面的資料夾之間的點分隔相對路徑。

例如,類別 PageModelPages/Customers/Edit.cshtml.cs 會明確設定命名空間:

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        // Code removed for brevity.

檔案 Pages/_ViewImports.cshtml 會設定下列命名空間:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Page 產生的命名空間 Pages/Customers/Edit.cshtmlRazor 與 類別相同 PageModel

@namespace也適用于傳統 Razor 檢視。

請考慮檢 Pages/Customers/Create.cshtml 視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Validation: customer name:</p>

<form method="post">
    <div asp-validation-summary="ModelOnly"></div>
    <span asp-validation-for="Customer.Name"></span>
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

Pages/Customers/Create.cshtml更新的檢視檔案,包含 _ViewImports.cshtml 和上述版面配置檔案:

@page
@model CreateModel

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

在上述程式碼中,已 _ViewImports.cshtml 匯入命名空間和標籤協助程式。 配置檔案已匯入 JavaScript 檔案。

Razor Pages 入門專案包含 Pages/_ValidationScriptsPartial.cshtml ,其會連結用戶端驗證。

如需部分檢視的詳細資訊,請參閱 ASP.NET Core 中的部分檢視

產生頁面 URL

前面出現過的 Create 頁面使用 RedirectToPage

public class CreateModel : PageModel
{
    private readonly CustomerDbContext _context;

    public CreateModel(CustomerDbContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Customers.Add(Customer);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

應用程式有下列檔案/資料夾結構:

  • /Pages

    • Index.cshtml

    • Privacy.cshtml

    • /Customers

      • Create.cshtml
      • Edit.cshtml
      • Index.cshtml

Pages/Customers/Create.cshtmlPages/Customers/Edit.cshtml 頁面在成功之後會重新導向至 Pages/Customers/Index.cshtml 。 字串 ./Index 是用來存取上一頁的相對頁面名稱。 它用來產生頁面的 Pages/Customers/Index.cshtml URL。 例如:

  • Url.Page("./Index", ...)
  • <a asp-page="./Index">Customers Index Page</a>
  • RedirectToPage("./Index")

絕對頁面名稱 /Index 用來產生頁面的 Pages/Index.cshtml URL。 例如:

  • Url.Page("/Index", ...)
  • <a asp-page="/Index">Home Index Page</a>
  • RedirectToPage("/Index")

頁面名稱是從根 /Pages 資料夾到該頁面的路徑 (包括前置的 /,例如 /Index)。 上述 URL 產生範例透過硬式編碼 URL 提供增強的選項和功能功能。 URL 產生使用路由,可以根據路由在目的地路徑中定義的方式,產生並且編碼參數。

產生頁面 URL 支援相關的名稱。 下表顯示使用 中 Pages/Customers/Create.cshtml 不同 RedirectToPage 參數選取的 [索引] 頁面。

RedirectToPage(x) 頁面
RedirectToPage("/Index") Pages/Index
RedirectToPage("./Index"); Pages/Customers/Index
RedirectToPage("../Index") Pages/Index
RedirectToPage("Index") Pages/Customers/Index

RedirectToPage("Index")RedirectToPage("./Index")RedirectToPage("../Index")相對名稱RedirectToPage 參數「結合」了目前頁面的路徑,以計算目的地頁面的名稱。

相對名稱連結在以複雜結構建置網站時很有用。 當使用相對名稱在資料夾中的頁面之間連結時:

  • 重新命名資料夾並不會中斷相對連結。
  • 連結不會中斷,因為它們不包含資料夾名稱。

若要重新導向到不同區域中的頁面,請指定區域:

RedirectToPage("/Index", new { area = "Services" });

如需詳細資訊,請參閱ASP.NET Core 中的區域和 Razor 頁面路由和應用程式慣例 ASP.NET Core

ViewData 屬性

資料可以傳遞至具有 ViewDataAttribute 的頁面。 具有 [ViewData] 屬性的屬性會儲存和載入其 ViewDataDictionary 值。

在下列範例中,會將 AboutModel 屬性套用 [ViewData]Title 屬性:

public class AboutModel : PageModel
{
    [ViewData]
    public string Title { get; } = "About";

    public void OnGet()
    {
    }
}

在 [關於] 頁面上,存取 Title 屬性作為模型屬性:

<h1>@Model.Title</h1>

在此配置中,標題會從 ViewData 字典中讀取:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

TempData

ASP.NET Core 會 TempData 公開 。 這個屬性會儲存資料,直到讀取為止。 KeepPeek 方法可以用來檢查資料,不用刪除。 TempData 對於重新導向很有用,當資料需要超過單一要求時。

下列程式碼會設定使用 TempDataMessage 值:

public class CreateDotModel : PageModel
{
    private readonly AppDbContext _db;

    public CreateDotModel(AppDbContext db)
    {
        _db = db;
    }

    [TempData]
    public string Message { get; set; }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _db.Customers.Add(Customer);
        await _db.SaveChangesAsync();
        Message = $"Customer {Customer.Name} added";
        return RedirectToPage("./Index");
    }
}

檔案中的 Pages/Customers/Index.cshtml 下列標記會顯示使用 TempData 的值 Message

<h3>Msg: @Model.Message</h3>

頁面 Pages/Customers/Index.cshtml.cs 模型會將 [TempData] 屬性套用至 Message 屬性。

[TempData]
public string Message { get; set; }

如需詳細資訊,請參閱 TempData

每頁面有多個處理常式

下列頁面會使用 asp-page-handler 標記協助程式為兩個處理常式產生標記:

@page
@model CreateFATHModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <!-- <snippet_Handlers> -->
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
        <!-- </snippet_Handlers> -->
    </form>
</body>
</html>

上例中的表單有兩個提交按鈕,每一個都使用 FormActionTagHelper 提交至不同的 URL。 asp-page-handler 屬性附隨於 asp-pageasp-page-handler 產生的 URL 會提交至頁面所定義的每一個處理常式方法。 因為範例連結至目前的頁面,所以未指定 asp-page

頁面模型:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages.Customers
{
    public class CreateFATHModel : PageModel
    {
        private readonly AppDbContext _db;

        public CreateFATHModel(AppDbContext db)
        {
            _db = db;
        }

        [BindProperty]
        public Customer Customer { get; set; }

        public async Task<IActionResult> OnPostJoinListAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Customers.Add(Customer);
            await _db.SaveChangesAsync();
            return RedirectToPage("/Index");
        }

        public async Task<IActionResult> OnPostJoinListUCAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
            Customer.Name = Customer.Name?.ToUpperInvariant();
            return await OnPostJoinListAsync();
        }
    }
}

上述程式碼使用「具名的處理常式方法」。 具名的處理常式方法的建立方式是採用名稱中在 On<HTTP Verb> 後面、Async 之前 (如有) 的文字。 在上例中,頁面方法是 OnPostJoinListAsync 和 OnPostJoinListUCAsync。 移除 OnPostAsync,處理常式名稱就是 JoinListJoinListUC

<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />

使用上述程式碼,提交至 OnPostJoinListAsync 的 URL 路徑是 https://localhost:5001/Customers/CreateFATH?handler=JoinList。 提交至 OnPostJoinListUCAsync 的 URL 路徑是 https://localhost:5001/Customers/CreateFATH?handler=JoinListUC

自訂路由

使用 @page 指示詞,可以:

  • 指定頁面的自訂路由。 例如,[關於] 頁面的路由可使用 @page "/Some/Other/Path" 設為 /Some/Other/Path
  • 將區段附加到頁面的預設路由。 例如,使用 @page "item" 可將 "item" 區段新增到頁面的預設路由。
  • 將參數附加到頁面的預設路由。 例如,具有 @page "{id}" 的頁面可要求識別碼參數 id

支援在路徑開頭以波狀符號 (~) 指定根相對路徑。 例如,@page "~/Some/Other/Path"@page "/Some/Other/Path" 相同。

如果您不喜歡 URL 中的查詢字串 ?handler=JoinList ,請變更路由,將處理常式名稱放在 URL 的路徑部分。 您可以藉由在 指示詞後面 @page 加上雙引號括住的路由範本來自訂路由。

@page "{handler?}"
@model CreateRouteModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

使用上述程式碼,提交至 OnPostJoinListAsync 的 URL 路徑是 https://localhost:5001/Customers/CreateFATH/JoinList。 提交至 OnPostJoinListUCAsync 的 URL 路徑是 https://localhost:5001/Customers/CreateFATH/JoinListUC

跟在 handler 後面的 ? 表示路由參數為選擇性。

進階組態和設定

大部分應用程式不需要下列各節中的組態和設定。

若要設定進階選項,請使用設定 的多 AddRazorPagesRazorPagesOptions 載:

using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages(options =>
{
    options.RootDirectory = "/MyPages";
    options.Conventions.AuthorizeFolder("/MyPages/Admin");
});

builder.Services.AddDbContext<CustomerDbContext>(options =>
    options.UseInMemoryDatabase("name"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

RazorPagesOptions使用 來設定頁面的根目錄,或為頁面新增應用程式模型慣例。 如需慣例的詳細資訊,請參閱Razor 頁面授權慣例

若要先行編譯檢視,請參閱Razor 檢視編譯

指定 Razor 頁面位於內容根目錄

根據預設, Razor Pages 會以 /Pages 目錄為根目錄。 新增 WithRazorPagesAtContentRoot 以指定您的 Razor Pages 位於應用程式 的內容根 目錄 (ContentRootPath) :

using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages(options =>
{
    options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
  .WithRazorPagesAtContentRoot();

builder.Services.AddDbContext<CustomerDbContext>(options =>
    options.UseInMemoryDatabase("name"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

指定 Razor Pages 位於自訂根目錄

新增 WithRazorPagesRoot 以指定 Razor Pages 位於應用程式中的自訂根目錄, (提供相對路徑) :

using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages(options =>
{
    options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
  .WithRazorPagesRoot("/path/to/razor/pages");

builder.Services.AddDbContext<CustomerDbContext>(options =>
    options.UseInMemoryDatabase("name"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

其他資源

建立 Razor Pages 專案

如需如何建立 Razor Pages 專案的詳細指示,請參閱開始使用 Razor Pages

Razor 頁面

Razor 在 中 Startup.cs 啟用頁面:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

請考慮使用基本頁面:

@page

<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>

上述程式碼看起來非常類似Razor ASP.NET Core 應用程式中使用控制器和檢視的檢視檔案。 讓它不同的是 @page 指示詞。 @page 會將檔案轉換成 MVC 動作,這表示它會直接處理要求,不用透過控制器。 @page 必須是頁面上的第一 Razor 個指示詞。 @page 會影響其他 Razor 建構的行為。 Razor 分頁檔名具有 .cshtml 尾碼。

使用PageModel類別的類似頁面,顯示於下列兩個檔案中。 Pages/Index2.cshtml 檔案:

@page
@using RazorPagesIntro.Pages
@model Index2Model

<h2>Separate page model</h2>
<p>
    @Model.Message
</p>

Pages/Index2.cshtml.cs頁面模型:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;

namespace RazorPagesIntro.Pages
{
    public class Index2Model : PageModel
    {
        public string Message { get; private set; } = "PageModel in C#";

        public void OnGet()
        {
            Message += $" Server time is { DateTime.Now }";
        }
    }
}

依照慣例,類別 PageModel 檔案的名稱與 Razor 附加的 Page 檔案 .cs 相同。 例如,上一 Razor 頁是 Pages/Index2.cshtml 。 包含 類別的 PageModel 檔案名為 Pages/Index2.cshtml.cs

頁面的 URL 路徑關聯是由頁面在檔案系統中的位置決定。 下表顯示 Razor 頁面路徑和相符的 URL:

檔案名稱和路徑 比對 URL
/Pages/Index.cshtml //Index
/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml /Store/Store/Index

注意:

  • 執行時間預設會在 Pages 資料夾中尋找 RazorPages 檔案。
  • Index 是 URL 未包含頁面時的預設頁面。

撰寫基本表單

Razor 頁面的設計目的是在建置應用程式時,輕鬆地實作與網頁瀏覽器搭配使用的常見模式。 模型系結標籤協助程式和HTML 協助程式全 都只使用 Page 類別中 Razor 定義的屬性。 Contact 模型請考慮實作基本的「與我們連絡」格式頁面:

本文件中的範例,會在 Startup.cs 檔案中初始化 DbContext

記憶體資料庫中的 Microsoft.EntityFrameworkCore.InMemory 需要 NuGet 套件。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<CustomerDbContext>(options =>
                      options.UseInMemoryDatabase("name"));
    services.AddRazorPages();
}

資料模型:

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Models
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(10)]
        public string Name { get; set; }
    }
}

DB 內容:

using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Models;

namespace RazorPagesContacts.Data
{
    public class CustomerDbContext : DbContext
    {
        public CustomerDbContext(DbContextOptions options)
            : base(options)
        {
        }

        public DbSet<Customer> Customers { get; set; }
    }
}

Pages/Create.cshtml 視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

Pages/Create.cshtml.cs頁面模型:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using RazorPagesContacts.Models;
using System.Threading.Tasks;

namespace RazorPagesContacts.Pages.Customers
{
    public class CreateModel : PageModel
    {
        private readonly CustomerDbContext _context;

        public CreateModel(CustomerDbContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Customer Customer { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Customers.Add(Customer);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

依照慣例,PageModel 類別稱之為 <PageName>Model,與頁面位於相同的命名空間。

PageModel 類別可以分離頁面邏輯與頁面展示。 此類別會定義頁面的處理常式,以處理傳送至頁面的要求與用於轉譯頁面的資料。 此區隔允許:

POST 要求上執行的頁面具有 OnPostAsync「處理常式方法」 (當使用者張貼表單時)。 您可以新增任何 HTTP 動詞命令的處理常式方法。 最常見的處理常式包括:

  • OnGet,初始化頁所需要的狀態。 在上述程式碼中 OnGet ,方法會顯示 CreateModel.cshtmlRazor Page。
  • OnPost,處理表單提交作業。

Async 命名尾碼是選擇性的,但依照慣例通常用於非同步函式。 上述程式碼是 Razor Pages 的典型程式碼。

如果您熟悉使用控制器和檢視 ASP.NET 應用程式:

  • 上述範例中的程式 OnPostAsync 代碼看起來類似于一般控制器程式碼。
  • 大部分的 MVC 基本類型,例如 模型系結驗證和動作結果,都與 Controllers 和 Razor Pages 相同。

前一個 OnPostAsync 方法:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Customers.Add(Customer);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

OnPostAsync 的基本流程:

檢查驗證錯誤。

  • 如果沒有任何錯誤,會儲存資料並重新導向。
  • 如果有錯誤,會再次顯示有驗證訊息的頁面。 在許多情況下,會在用戶端上偵測到驗證錯誤,且永遠不會提交到伺服器。

Pages/Create.cshtml 視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

從 轉譯的 HTML Pages/Create.cshtml

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input type="text" data-val="true"
           data-val-length="The field Name must be a string with a maximum length of 10."
           data-val-length-max="10" data-val-required="The Name field is required."
           id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
    <input type="submit" />
    <input name="__RequestVerificationToken" type="hidden"
           value="<Antiforgery token here>" />
</form>

在先前的程式碼中,張貼表單:

  • 使用有效的資料:

    • OnPostAsync處理常式方法會 RedirectToPage 呼叫協助程式方法。 RedirectToPage 會傳回 RedirectToPageResult 的執行個體。 RedirectToPage:

      • 這是動作結果。
      • RedirectToAction類似于或 RedirectToRoute (用於控制器和檢視) 。
      • 已針對頁面自訂。 在上述範例中,它會重新導向至根索引頁面 (/Index)。 產生頁面 URL一節會詳細說明 RedirectToPage
  • 傳遞至伺服器的驗證錯誤:

    • OnPostAsync處理常式方法會 Page 呼叫協助程式方法。 Page 會傳回 PageResult 的執行個體。 傳回 Page 類似於控制站中的動作傳回 ViewPageResult 是處理常式方法的預設傳回型別。 傳回 void 的處理常式方法會呈現頁面。
    • 在上述範例中,張貼沒有值的表單會導致 ModelState.IsValid 傳回 false。 在此範例中,用戶端上不會顯示任何驗證錯誤。 本檔稍後涵蓋驗證錯誤交握。
    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
    
        _context.Customers.Add(Customer);
        await _context.SaveChangesAsync();
    
        return RedirectToPage("./Index");
    }
    
  • 用戶端驗證偵測到驗證錯誤:

    • 資料 不會 張貼到伺服器。
    • 本文稍後會說明用戶端驗證。

屬性 Customer 會使用 [BindProperty] 屬性加入宣告模型系結:

public class CreateModel : PageModel
{
    private readonly CustomerDbContext _context;

    public CreateModel(CustomerDbContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Customers.Add(Customer);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

[BindProperty]不應該用於包含用戶端不應變更之屬性的模型。 如需詳細資訊,請參閱 Overposting

Razor 根據預設,頁面只會使用非 GET 動詞系結屬性。 系結至屬性會移除撰寫程式碼以將 HTTP 資料轉換成模型類型的需求。 透過使用相同的屬性呈現表單欄位 (<input asp-for="Customer.Name">) 並接受輸入,繫結可以減少程式碼。

警告

基於安全性考量,您必須選擇將 GET 要求資料繫結到頁面模型屬性。 請先驗證使用者輸入再將其對應至屬性。 在處理依賴查詢字串或路由值的案例時,加入宣告 GET 系結很有用。

若要在要求上 GET 系結屬性,請將 [BindProperty] 屬性的 SupportsGet 屬性設定為 true

[BindProperty(SupportsGet = true)]

如需詳細資訊,請參閱 ASP.NET Core Community Standup: Bind on GET 討論 (YouTube)

Pages/Create.cshtml 閱檢視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>
  • 在上述程式碼中,輸入標籤協助程式<input asp-for="Customer.Name" /> 會將 HTML <input> 元素系結至 Customer.Name 模型運算式。
  • @addTagHelper 讓標籤協助程式可供使用。

首頁

Index.cshtml 是首頁:

@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<h1>Contacts home page</h1>
<form method="post">
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @foreach (var contact in Model.Customer)
            {
                <tr>
                    <td> @contact.Id  </td>
                    <td>@contact.Name</td>
                    <td>
                        <!-- <snippet_Edit> -->
                        <a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
                        <!-- </snippet_Edit> -->
                        <!-- <snippet_Delete> -->
                        <button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
                        <!-- </snippet_Delete> -->
                    </td>
                </tr>
            }
        </tbody>
    </table>
    <a asp-page="Create">Create New</a>
</form>

相關聯的 PageModel 類別 (Index.cshtml.cs) :

public class IndexModel : PageModel
{
    private readonly CustomerDbContext _context;

    public IndexModel(CustomerDbContext context)
    {
        _context = context;
    }

    public IList<Customer> Customer { get; set; }

    public async Task OnGetAsync()
    {
        Customer = await _context.Customers.ToListAsync();
    }

    public async Task<IActionResult> OnPostDeleteAsync(int id)
    {
        var contact = await _context.Customers.FindAsync(id);

        if (contact != null)
        {
            _context.Customers.Remove(contact);
            await _context.SaveChangesAsync();
        }

        return RedirectToPage();
    }
}

檔案 Index.cshtml 包含下列標記:

<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |

<a /a>錨點標籤協助程式使用 asp-route-{value} 屬性來產生編輯頁面的連結。 該連結包含路由資料和連絡人識別碼。 例如: https://localhost:5001/Edit/1標籤協助程式 可讓伺服器端程式碼參與在檔案中 Razor 建立和轉譯 HTML 元素。

檔案 Index.cshtml 包含標記,可為每個客戶連絡人建立刪除按鈕:

<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>

轉譯的 HTML:

<button type="submit" formaction="/Customers?id=1&amp;handler=delete">delete</button>

當刪除按鈕以 HTML 呈現時,其 formaction 會包含下列參數:

  • 由 屬性指定的 asp-route-id 客戶連絡人識別碼。
  • 屬性 handlerasp-page-handler 指定的 。

選取按鈕時,表單 POST 要求會傳送至伺服器。 依照慣例,會依據配置 OnPost[handler]Async,按 handler 參數的值來選取處理常式方法。

在此範例中,因為 handlerdelete,所以會使用 OnPostDeleteAsync 處理常式方法來處理 POST 要求。 若 asp-page-handler 設為其他值 (例如 remove),則會選取名為 OnPostRemoveAsync 的處理常式方法。

public async Task<IActionResult> OnPostDeleteAsync(int id)
{
    var contact = await _context.Customers.FindAsync(id);

    if (contact != null)
    {
        _context.Customers.Remove(contact);
        await _context.SaveChangesAsync();
    }

    return RedirectToPage();
}

OnPostDeleteAsync 方法:

  • id從查詢字串取得 。
  • 使用 FindAsync 在資料庫中查詢客戶連絡人。
  • 如果找到客戶連絡人,則會移除並更新資料庫。
  • 呼叫 RedirectToPage 以重新導向至根索引頁 (/Index)。

Edit.cshtml 檔案

@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers


<h1>Edit Customer - @Model.Customer.Id</h1>
<form method="post">
    <div asp-validation-summary="All"></div>
    <input asp-for="Customer.Id" type="hidden" />
    <div>
        <label asp-for="Customer.Name"></label>
        <div>
            <input asp-for="Customer.Name" />
            <span asp-validation-for="Customer.Name"></span>
        </div>
    </div>

    <div>
        <button type="submit">Save</button>
    </div>
</form>

第一行包含 @page "{id:int}" 指示詞。 路由條件約束 "{id:int}" 會指示頁面接受包含 int 路由資料之頁面的要求。 如果頁面要求不包含可以轉換成 int 的路由資料,執行階段會傳回 HTTP 404 (找不到) 錯誤。 若要使識別碼成為選擇性,請將 ? 附加至路由條件約束:

@page "{id:int?}"

Edit.cshtml.cs 檔案:

public class EditModel : PageModel
{
    private readonly CustomerDbContext _context;

    public EditModel(CustomerDbContext context)
    {
        _context = context;
    }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnGetAsync(int id)
    {
        Customer = await _context.Customers.FindAsync(id);

        if (Customer == null)
        {
            return RedirectToPage("./Index");
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Attach(Customer).State = EntityState.Modified;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            throw new Exception($"Customer {Customer.Id} not found!");
        }

        return RedirectToPage("./Index");
    }

}

驗證

驗證規則:

  • 在模型類別中以宣告方式指定。
  • 會在應用程式中的任何地方強制執行。

System.ComponentModel.DataAnnotations命名空間提供一組內建驗證屬性,這些屬性會以宣告方式套用至類別或屬性。 DataAnnotations 也包含格式化屬性 (如 [DataType]),可協助進行格式化,但不提供任何驗證。

請考慮模型 Customer

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Models
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(10)]
        public string Name { get; set; }
    }
}

使用下列 Create.cshtml 檢視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Validation: customer name:</p>

<form method="post">
    <div asp-validation-summary="ModelOnly"></div>
    <span asp-validation-for="Customer.Name"></span>
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

上述程式碼:

  • 包含 jQuery 和 jQuery 驗證腳本。

  • <div />使用 和 <span />標籤協助程式來啟用:

    • 用戶端驗證。
    • 驗證錯誤轉譯。
  • 產生下列 HTML:

    <p>Enter a customer name:</p>
    
    <form method="post">
        Name:
        <input type="text" data-val="true"
               data-val-length="The field Name must be a string with a maximum length of 10."
               data-val-length-max="10" data-val-required="The Name field is required."
               id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
        <input type="submit" />
        <input name="__RequestVerificationToken" type="hidden"
               value="<Antiforgery token here>" />
    </form>
    
    <script src="/lib/jquery/dist/jquery.js"></script>
    <script src="/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
    

張貼沒有名稱值的 [建立表單] 會顯示表單上的錯誤訊息「需要名稱欄位」。 如果在用戶端上啟用 JavaScript,瀏覽器就會顯示錯誤,而不會張貼到伺服器。

屬性 [StringLength(10)] 會在 data-val-length-max="10" 轉譯的 HTML 上產生。 data-val-length-max 防止瀏覽器輸入超過指定的長度上限。 如果使用 Fiddler 之類的工具來編輯並重新執行文章:

  • 名稱超過 10。
  • 傳回「功能變數名稱必須是長度上限為 10 的字串」錯誤訊息。

請考慮下列 Movie 模型:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }

        [StringLength(60, MinimumLength = 3)]
        [Required]
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [Range(1, 100)]
        [DataType(DataType.Currency)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
        [Required]
        [StringLength(30)]
        public string Genre { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
        [StringLength(5)]
        [Required]
        public string Rating { get; set; }
    }
}

驗證屬性會指定要在套用至的模型屬性上強制執行的行為:

  • RequiredMinimumLength 屬性工作表示屬性必須有值,但不會防止使用者輸入空白字元來滿足此驗證。

  • RegularExpression 屬性則用來限制可輸入的字元。 在上述程式碼中,"Genre":

    • 必須指使用字母。
    • 第一個字母必須是大寫。 不允許使用空格、數字和特殊字元。
  • RegularExpression "Rating":

    • 第一個字元必須為大寫字母。
    • 允許後續空格中的特殊字元和數位。 "PG-13" 對分級而言有效,但不適用於 "Genre"。
  • Range 屬性會將值限制在指定的範圍內。

  • 屬性 StringLength 會設定字串屬性的最大長度,並選擇性地設定其最小長度。

  • 實值型別 (如decimalintfloatDateTime) 原本就是必要項目,而且不需要 [Required] 屬性。

模型的 [ Movie 建立] 頁面會顯示具有無效值的錯誤:

Movie view form with multiple jQuery client-side validation errors

如需詳細資訊,請參閱

使用 OnGet 處理常式後援來處理 HEAD 要求

HEAD 要求允許擷取特定資源的標頭。 不同於 GET 要求,HEAD 要求不會傳回回應主體。

一般來說,會為 HEAD 要求建立及呼叫 OnHead 處理常式:

public void OnHead()
{
    HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}

Razor如果未 OnHead 定義處理常式, OnGet 頁面會回復為呼叫處理常式。

XSRF/CSRF 和 Razor 頁面

Razor 頁面受到 Antiforgery 驗證的保護。 FormTagHelper會將反移轉權杖插入 HTML 表單元素中。

搭配頁面使用版面配置、部分、範本和標籤協助程式 Razor

頁面會使用檢視引擎的所有功能 Razor 。 版面配置、部分、範本、標籤協助程式、 _ViewStart.cshtml_ViewImports.cshtml 的運作方式與傳統 Razor 檢視相同。

可利用這些功能的一部分來整理這個頁面。

版面配置頁面 新增至 Pages/Shared/_Layout.cshtml

<!DOCTYPE html>
<html>
<head>
    <title>RP Sample</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
    <a asp-page="/Index">Home</a>
    <a asp-page="/Customers/Create">Create</a>
    <a asp-page="/Customers/Index">Customers</a> <br />

    @RenderBody()
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>

面配置

  • 控制每個頁面的版面配置 (除非頁面退出版面配置)。
  • 匯入 HTML 結構,例如 JavaScript 和樣式表。
  • 頁面的內容 Razor 會在呼叫的位置 @RenderBody() 轉譯。

如需詳細資訊,請參閱 版面配置頁面

Layout屬性是在 中 Pages/_ViewStart.cshtml 設定:

@{
    Layout = "_Layout";
}

版面配置位於 Pages/Shared 資料夾。 頁面會以階層方式尋找其他檢視 (版面配置、範本、部分),從目前頁面的相同資料夾開始。 Pages/Shared資料夾中的版面配置可從Pages資料夾下的任何 Razor 頁面使用。

版面配置頁面應位於 Pages/Shared 資料夾中。

我們建議您將配置檔案放入 Views/Shared 資料夾。 Views/Shared 是 MVC 檢視模式。 Razor 頁面的目的是依賴資料夾階層,而不是路徑慣例。

從 Razor 頁面檢視搜尋包含 Pages 資料夾。 與 MVC 控制器和傳統 Razor 檢視搭配使用的版面配置、範本和部分 僅能運作

新增檔案 Pages/_ViewImports.cshtml

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

本教學課程稍後會說明 @namespace@addTagHelper 指示詞會將內建標記協助程式帶入 Pages 資料夾中的所有頁面。

@namespace在頁面上設定的 指示詞:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

<h2>Name space</h2>
<p>
    @Model.Message
</p>

指示詞 @namespace 會設定頁面的命名空間。 @model 指示詞不需要包含命名空間。

@namespace當 指示詞包含在 中 _ViewImports.cshtml 時,指定的命名空間會在匯入 指示詞的 Page @namespace 中提供所產生命名空間的前置詞。 產生的命名空間的其餘部分 (尾碼部分) 是包含 _ViewImports.cshtml 資料夾與包含頁面的資料夾之間的點分隔相對路徑。

例如,類別 PageModelPages/Customers/Edit.cshtml.cs 會明確設定命名空間:

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        // Code removed for brevity.

檔案 Pages/_ViewImports.cshtml 會設定下列命名空間:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Page 產生的命名空間 Pages/Customers/Edit.cshtmlRazor 與 類別相同 PageModel

@namespace也適用于傳統 Razor 檢視。

請考慮檢 Pages/Create.cshtml 視檔案:

@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Validation: customer name:</p>

<form method="post">
    <div asp-validation-summary="ModelOnly"></div>
    <span asp-validation-for="Customer.Name"></span>
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

Pages/Create.cshtml更新的檢視檔案,包含 _ViewImports.cshtml 和上述版面配置檔案:

@page
@model CreateModel

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

在上述程式碼中,匯 _ViewImports.cshtml 入命名空間和標籤協助程式。 配置檔已匯入 JavaScript 檔案。

Razor Pages 入門專案包含 , Pages/_ValidationScriptsPartial.cshtml 它會連結用戶端驗證。

如需部分檢視的詳細資訊,請參閱 ASP.NET Core 中的部分檢視

產生頁面 URL

前面出現過的 Create 頁面使用 RedirectToPage

public class CreateModel : PageModel
{
    private readonly CustomerDbContext _context;

    public CreateModel(CustomerDbContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Customers.Add(Customer);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

應用程式有下列檔案/資料夾結構:

  • /Pages

    • Index.cshtml

    • Privacy.cshtml

    • /Customers

      • Create.cshtml
      • Edit.cshtml
      • Index.cshtml

Pages/Customers/Create.cshtmlPages/Customers/Edit.cshtml 頁面在成功之後會重新導向至 Pages/Customers/Index.cshtml 。 字串 ./Index 是用來存取上一頁的相對頁面名稱。 它用來產生頁面的 Pages/Customers/Index.cshtml URL。 例如:

  • Url.Page("./Index", ...)
  • <a asp-page="./Index">Customers Index Page</a>
  • RedirectToPage("./Index")

絕對頁面名稱 /Index 可用來產生頁面的 Pages/Index.cshtml URL。 例如:

  • Url.Page("/Index", ...)
  • <a asp-page="/Index">Home Index Page</a>
  • RedirectToPage("/Index")

頁面名稱是從根 /Pages 資料夾到該頁面的路徑 (包括前置的 /,例如 /Index)。 上述 URL 產生範例透過硬式編碼 URL 提供增強的選項和功能功能。 URL 產生使用路由,可以根據路由在目的地路徑中定義的方式,產生並且編碼參數。

產生頁面 URL 支援相關的名稱。 下表顯示使用 中 Pages/Customers/Create.cshtml 不同 RedirectToPage 參數選取的 [索引] 頁面。

RedirectToPage(x) 頁面
RedirectToPage("/Index") Pages/Index
RedirectToPage("./Index"); Pages/Customers/Index
RedirectToPage("../Index") Pages/Index
RedirectToPage("Index") Pages/Customers/Index

RedirectToPage("Index")RedirectToPage("./Index")RedirectToPage("../Index")相對名稱RedirectToPage 參數「結合」了目前頁面的路徑,以計算目的地頁面的名稱。

相對名稱連結在以複雜結構建置網站時很有用。 當相對名稱用來連結資料夾中的頁面時:

  • 重新命名資料夾不會中斷相對連結。
  • 連結不會中斷,因為它們不包含資料夾名稱。

若要重新導向到不同區域中的頁面,請指定區域:

RedirectToPage("/Index", new { area = "Services" });

如需詳細資訊,請參閱ASP.NET Core 中的 ASP.NET 核心頁面路由和 Razor 應用程式慣例的區域

ViewData 屬性

資料可以使用 傳遞至頁面 ViewDataAttribute 。 具有 屬性的屬性 [ViewData] 會從 儲存和載入 ViewDataDictionary 其值。

在下列範例中,會將 AboutModel 屬性套用 [ViewData]Title 屬性:

public class AboutModel : PageModel
{
    [ViewData]
    public string Title { get; } = "About";

    public void OnGet()
    {
    }
}

在 [關於] 頁面上,存取 Title 屬性作為模型屬性:

<h1>@Model.Title</h1>

在此配置中,標題會從 ViewData 字典中讀取:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

TempData

ASP.NET Core 會 TempData 公開 。 這個屬性會儲存資料,直到讀取為止。 KeepPeek 方法可以用來檢查資料,不用刪除。 TempData 當需要超過單一要求的資料時,重新導向會很有用。

下列程式碼會設定使用 TempDataMessage 值:

public class CreateDotModel : PageModel
{
    private readonly AppDbContext _db;

    public CreateDotModel(AppDbContext db)
    {
        _db = db;
    }

    [TempData]
    public string Message { get; set; }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _db.Customers.Add(Customer);
        await _db.SaveChangesAsync();
        Message = $"Customer {Customer.Name} added";
        return RedirectToPage("./Index");
    }
}

檔案中的 Pages/Customers/Index.cshtml 下列標記會顯示 使用 TempData 的值 Message

<h3>Msg: @Model.Message</h3>

頁面 Pages/Customers/Index.cshtml.cs 模型會將 [TempData] 屬性套用至 Message 屬性。

[TempData]
public string Message { get; set; }

如需詳細資訊,請參閱 TempData

每頁面有多個處理常式

下列頁面會使用 asp-page-handler 標記協助程式為兩個處理常式產生標記:

@page
@model CreateFATHModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <!-- <snippet_Handlers> -->
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
        <!-- </snippet_Handlers> -->
    </form>
</body>
</html>

上例中的表單有兩個提交按鈕,每一個都使用 FormActionTagHelper 提交至不同的 URL。 asp-page-handler 屬性附隨於 asp-pageasp-page-handler 產生的 URL 會提交至頁面所定義的每一個處理常式方法。 因為範例連結至目前的頁面,所以未指定 asp-page

頁面模型:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages.Customers
{
    public class CreateFATHModel : PageModel
    {
        private readonly AppDbContext _db;

        public CreateFATHModel(AppDbContext db)
        {
            _db = db;
        }

        [BindProperty]
        public Customer Customer { get; set; }

        public async Task<IActionResult> OnPostJoinListAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Customers.Add(Customer);
            await _db.SaveChangesAsync();
            return RedirectToPage("/Index");
        }

        public async Task<IActionResult> OnPostJoinListUCAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
            Customer.Name = Customer.Name?.ToUpperInvariant();
            return await OnPostJoinListAsync();
        }
    }
}

上述程式碼使用「具名的處理常式方法」。 具名的處理常式方法的建立方式是採用名稱中在 On<HTTP Verb> 後面、Async 之前 (如有) 的文字。 在上例中,頁面方法是 OnPostJoinListAsync 和 OnPostJoinListUCAsync。 移除 OnPostAsync,處理常式名稱就是 JoinListJoinListUC

<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />

使用上述程式碼,提交至 OnPostJoinListAsync 的 URL 路徑是 https://localhost:5001/Customers/CreateFATH?handler=JoinList。 提交至 OnPostJoinListUCAsync 的 URL 路徑是 https://localhost:5001/Customers/CreateFATH?handler=JoinListUC

自訂路由

使用 @page 指示詞,可以:

  • 指定頁面的自訂路由。 例如,[關於] 頁面的路由可使用 @page "/Some/Other/Path" 設為 /Some/Other/Path
  • 將區段附加到頁面的預設路由。 例如,使用 @page "item" 可將 "item" 區段新增到頁面的預設路由。
  • 將參數附加到頁面的預設路由。 例如,具有 @page "{id}" 的頁面可要求識別碼參數 id

支援在路徑開頭以波狀符號 (~) 指定根相對路徑。 例如,@page "~/Some/Other/Path"@page "/Some/Other/Path" 相同。

如果您不喜歡 URL 中的查詢字串 ?handler=JoinList ,請變更路由,將處理常式名稱放在 URL 的路徑部分。 您可以藉由在 指示詞後面加上雙引號括住的 @page 路由範本來自訂路由。

@page "{handler?}"
@model CreateRouteModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

使用上述程式碼,提交至 OnPostJoinListAsync 的 URL 路徑是 https://localhost:5001/Customers/CreateFATH/JoinList。 提交至 OnPostJoinListUCAsync 的 URL 路徑是 https://localhost:5001/Customers/CreateFATH/JoinListUC

跟在 handler 後面的 ? 表示路由參數為選擇性。

進階組態和設定

大部分應用程式不需要下列各節中的組態和設定。

若要設定進階選項,請使用 AddRazorPages 設定 的多 RazorPagesOptions 載:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages(options =>
    {
        options.RootDirectory = "/MyPages";
        options.Conventions.AuthorizeFolder("/MyPages/Admin");
    });
}

RazorPagesOptions使用 來設定頁面的根目錄,或新增頁面的應用程式模型慣例。 如需慣例的詳細資訊,請參閱Razor 頁面授權慣例

若要預先編譯檢視,請參閱Razor 檢視編譯

指定 Razor Pages 位於內容根目錄

根據預設, Razor Pages 會以 /Pages 目錄為根目錄。 新增 WithRazorPagesAtContentRoot 以指定您的 Razor Pages 位於應用程式 的內容根 目錄 (ContentRootPath) :

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages(options =>
        {
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        })
        .WithRazorPagesAtContentRoot();
}

指定 Razor Pages 位於自訂根目錄

新增 WithRazorPagesRoot 以指定 Razor Pages 位於應用程式中的自訂根目錄, (提供相對路徑) :

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages(options =>
        {
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        })
        .WithRazorPagesRoot("/path/to/razor/pages");
}

其他資源