ASP.NET Core 中的 Razor Pages 簡介Introduction to Razor Pages in ASP.NET Core

作者:Rick AndersonRyan NowakBy Rick Anderson and Ryan Nowak

Razor Pages 是 ASP.NET Core MVC 新的部分,更容易編寫以頁面為焦點的案例程式碼,也更具生產力。Razor Pages is a new aspect of ASP.NET Core MVC that makes coding page-focused scenarios easier and more productive.

如果您在尋找使用模型檢視控制器方法的教學課程,請參閱開始使用 ASP.NET Core MVCIf you're looking for a tutorial that uses the Model-View-Controller approach, see Get started with ASP.NET Core MVC.

本文件提供 Razor Pages 簡介。This document provides an introduction to Razor Pages. 它不是逐步教學課程。It's not a step by step tutorial. 如果您發現某些章節很難遵循,請參閱9開始使用 Razor PagesIf you find some of the sections too advanced, see Get started with Razor Pages. 如需 ASP.NET Core 的概觀,請參閱ASP.NET Core 簡介For an overview of ASP.NET Core, see the Introduction to ASP.NET Core.

必要條件Prerequisites

必要條件Prerequisites

建立 Razor Pages 專案Create a Razor Pages project

如需如何建立 Razor Pages 專案的詳細說明,請參閱開始使用 Razor PagesSee Get started with Razor Pages for detailed instructions on how to create a Razor Pages project.

Razor 頁面Razor Pages

Razor Pages 是在 Startup.cs 中啟用:Razor Pages is enabled in Startup.cs:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Includes support for Razor Pages and controllers.
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc();
    }
}

請考慮使用基本頁面:Consider a basic page:

@page

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

上述程式碼看起來很像用於 ASP.NET Core 應用程式的 Razor 檢視檔案,含有控制器和檢視。The preceding code looks a lot like a Razor view file used in an ASP.NET Core app with controllers and views. 讓它不同的是 @page 指示詞。What makes it different is the @page directive. @page 會將檔案轉換成 MVC 動作,這表示它會直接處理要求,不用透過控制器。@page makes the file into an MVC action - which means that it handles requests directly, without going through a controller. @page 必須是頁面上的第一個 Razor 指示詞。@page must be the first Razor directive on a page. @page 會影響其他的 Razor 建構行為。@page affects the behavior of other Razor constructs.

使用PageModel類別的類似頁面,顯示於下列兩個檔案中。A similar page, using a PageModel class, is shown in the following two files. Pages/Index2.cshtml 檔案:The Pages/Index2.cshtml file:

@page
@using RazorPagesIntro.Pages
@model IndexModel2

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

Pages/Index2.cshtml.cs 頁面模型:The Pages/Index2.cshtml.cs page model:

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

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

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

依照慣例,PageModel 類別檔和附加 .cs 檔名的 Razor Page 檔案名稱相同。By convention, the PageModel class file has the same name as the Razor Page file with .cs appended. 例如,前一個 Razor Page 是 Pages/Index2.cshtmlFor example, the previous Razor Page is Pages/Index2.cshtml. 包含 PageModel 類別的檔案名為 Pages/Index2.cshtml.csThe file containing the PageModel class is named Pages/Index2.cshtml.cs.

頁面的 URL 路徑關聯是由頁面在檔案系統中的位置決定。The associations of URL paths to pages are determined by the page's location in the file system. 下表顯示 Razor 頁面路徑和相符的 URL:The following table shows a Razor Page path and the matching URL:

檔案名稱和路徑File name and path 比對 URLmatching URL
/Pages/Index.cshtml/Pages/Index.cshtml //Index/ or /Index
/Pages/Contact.cshtml/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml/Pages/Store/Index.cshtml /Store/Store/Index/Store or /Store/Index

附註:Notes:

  • 執行階段預設會在 Pages 資料夾中尋找 Razor Pages 的檔案。The runtime looks for Razor Pages files in the Pages folder by default.
  • Index 是 URL 未包含頁面時的預設頁面。Index is the default page when a URL doesn't include a page.

撰寫基本表單Write a basic form

Razor Pages 設計用於製作一般的模式,可搭配網頁瀏覽器一起使用,在建置應用程式時能易於實作。Razor Pages is designed to make common patterns used with web browsers easy to implement when building an app. 模型繫結標記協助程式和 HTML 協助程式搭配 Razor Page 類別中定義的屬性「就這麼簡單」。Model binding, Tag Helpers, and HTML helpers all just work with the properties defined in a Razor Page class. Contact 模型請考慮實作基本的「與我們連絡」格式頁面:Consider a page that implements a basic "contact us" form for the Contact model:

本文件中的範例,會在 Startup.cs 檔案中初始化 DbContextFor the samples in this document, the DbContext is initialized in the Startup.cs file.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RazorPagesContacts.Data;

namespace RazorPagesContacts
{
    public class Startup
    {
        public IHostingEnvironment HostingEnvironment { get; }

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

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc();
        }
    }
}

資料模型:The data model:

using System.ComponentModel.DataAnnotations;

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

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

DB 內容:The db context:

using Microsoft.EntityFrameworkCore;

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

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

Pages/Create.cshtml 檢視檔案:The Pages/Create.cshtml view file:

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

<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" />
    </form>
</body>
</html>

Pages/Create.cshtml.cs 頁面模型:The Pages/Create.cshtml.cs page model:

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

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

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

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

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

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

依照慣例,PageModel 類別稱之為 <PageName>Model,與頁面位於相同的命名空間。By convention, the PageModel class is called <PageName>Model and is in the same namespace as the page.

PageModel 類別可以分離頁面邏輯與頁面展示。The PageModel class allows separation of the logic of a page from its presentation. 此類別會定義頁面的處理常式,以處理傳送至頁面的要求與用於轉譯頁面的資料。It defines page handlers for requests sent to the page and the data used to render the page. 分離頁面邏輯與頁面展示可讓您透過相依性插入來管理頁面相依性,以及針對頁面進行單元測試This separation allows you to manage page dependencies through dependency injection and to unit test the pages.

POST 要求上執行的頁面具有 OnPostAsync「處理常式方法」 (當使用者張貼表單時)。The page has an OnPostAsync handler method, which runs on POST requests (when a user posts the form). 您可以新增任何 HTTP 指令動詞的處理常式方法。You can add handler methods for any HTTP verb. 最常見的處理常式包括:The most common handlers are:

  • OnGet,初始化頁所需要的狀態。OnGet to initialize state needed for the page. OnGet 範例。OnGet sample.
  • OnPost,處理表單提交作業。OnPost to handle form submissions.

Async 命名尾碼是選擇性的,但依照慣例通常用於非同步函式。The Async naming suffix is optional but is often used by convention for asynchronous functions. 上例中的 OnPostAsync 程式碼看起來類似您一般在控制器中撰寫的內容。The OnPostAsync code in the preceding example looks similar to what you would normally write in a controller. 上述程式碼一般用於 Razor 頁面。The preceding code is typical for Razor Pages. 大部分的基本 MVC,像是模型繫結驗證和動作結果都是共用的。Most of the MVC primitives like model binding, validation, and action results are shared.

前一個 OnPostAsync 方法:The previous OnPostAsync method:

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

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

OnPostAsync 的基本流程:The basic flow of OnPostAsync:

檢查驗證錯誤。Check for validation errors.

  • 如果沒有任何錯誤,會儲存資料並重新導向。If there are no errors, save the data and redirect.
  • 如果有錯誤,會再次顯示有驗證訊息的頁面。If there are errors, show the page again with validation messages. 用戶端驗證和傳統的 ASP.NET Core MVC 應用程式完全相同。Client-side validation is identical to traditional ASP.NET Core MVC applications. 在許多情況下,用戶端會偵測到驗證錯誤,但從不提交給伺服器。In many cases, validation errors would be detected on the client, and never submitted to the server.

成功輸入資料後,OnPostAsync 處理常式方法會呼叫 RedirectToPage 協助程式方法,傳回 RedirectToPageResult 的執行個體。When the data is entered successfully, the OnPostAsync handler method calls the RedirectToPage helper method to return an instance of RedirectToPageResult. RedirectToPage 是新的動作結果,類似於 RedirectToActionRedirectToRoute,但會針對頁面自訂。RedirectToPage is a new action result, similar to RedirectToAction or RedirectToRoute, but customized for pages. 在上述範例中,它會重新導向至根索引頁面 (/Index)。In the preceding sample, it redirects to the root Index page (/Index). 產生頁面 URL一節會詳細說明 RedirectToPageRedirectToPage is detailed in the URL generation for Pages section.

當提交的表單有驗證錯誤時 (傳遞至伺服器),OnPostAsync 處理常式方法會呼叫 Page 協助程式方法。When the submitted form has validation errors (that are passed to the server), theOnPostAsync handler method calls the Page helper method. Page 傳回 PageResult 的執行個體。Page returns an instance of PageResult. 傳回 Page 類似於控制站中的動作傳回 ViewReturning Page is similar to how actions in controllers return View. PageResult 處理常式方法PageResult is the default 的預設傳回型別。return type for a handler method. 傳回 void 的處理常式方法會呈現頁面。A handler method that returns void renders the page.

Customer 屬性 (property) 使用 [BindProperty] 屬性 (attribute) 加入模型繫結。The Customer property uses [BindProperty] attribute to opt in to model binding.

public class CreateModel : PageModel
{
    private readonly AppDbContext _db;

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

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

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

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

Razor Pages 預設只繫結屬性和非 GET 指令動詞。Razor Pages, by default, bind properties only with non-GET verbs. 繫結至屬性可以減少您必須撰寫的程式碼數量。Binding to properties can reduce the amount of code you have to write. 透過使用相同的屬性呈現表單欄位 (<input asp-for="Customer.Name">) 並接受輸入,繫結可以減少程式碼。Binding reduces code by using the same property to render form fields (<input asp-for="Customer.Name">) and accept the input.

警告

基於安全性考量,您必須選擇將 GET 要求資料繫結到頁面模型屬性。For security reasons, you must opt in to binding GET request data to page model properties. 請先驗證使用者輸入再將其對應至屬性。Verify user input before mapping it to properties. 在解決仰賴查詢字串或路由值的案例時,選擇使用 GET 繫結會很有幫助。Opting in to GET binding is useful when addressing scenarios which rely on query string or route values.

若要在 GET 要求上繫結屬性,請將 [BindProperty]屬性的 SupportsGet 屬性設定為 true[BindProperty(SupportsGet = true)]To bind a property on GET requests, set the [BindProperty] attribute's SupportsGet property to true: [BindProperty(SupportsGet = true)]

首頁 (Index.cshtml):The home page (Index.cshtml):

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

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

    <a asp-page="./Create">Create</a>
</form>

已建立關聯的 PageModel 類別 (Index.cshtml.cs):The associated PageModel class (Index.cshtml.cs):

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

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

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

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

        public async Task OnGetAsync()
        {
            Customers = await _db.Customers.AsNoTracking().ToListAsync();
        }

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

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

            return RedirectToPage();
        }
    }
}

Index.cshtml 檔案包含下列標記可為每個連絡人建立編輯連結:The Index.cshtml file contains the following markup to create an edit link for each contact:

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

錨定標記協助程式過去使用 asp-route-{value} 屬性產生 [編輯] 頁面的連結。The Anchor Tag Helper used the asp-route-{value} attribute to generate a link to the Edit page. 該連結包含路由資料和連絡人識別碼。The link contains route data with the contact ID. 例如,http://localhost:5000/Edit/1For example, http://localhost:5000/Edit/1. 使用 asp-area 屬性來指定區域。Use the asp-area attribute to specify an area. 如需詳細資訊,請參閱ASP.NET Core 中的區域For more information, see ASP.NET Core 中的區域.

Pages/Edit.cshtml 檔案:The Pages/Edit.cshtml file:

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

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

<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}" 指示詞。The first line contains the @page "{id:int}" directive. 路由條件約束 "{id:int}" 通知頁面接受包含 int 路由資料的頁面要求。The routing constraint"{id:int}" tells the page to accept requests to the page that contain int route data. 如果頁面要求不包含可以轉換成 int 的路由資料,執行階段會傳回 HTTP 404 (找不到) 錯誤。If a request to the page doesn't contain route data that can be converted to an int, the runtime returns an HTTP 404 (not found) error. 若要使識別碼成為選擇性,請將 ? 附加至路由條件約束:To make the ID optional, append ? to the route constraint:

@page "{id:int?}"

Pages/Edit.cshtml.cs 檔案:The Pages/Edit.cshtml.cs file:

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

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

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

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

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

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

            return Page();
        }

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

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

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

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

Index.cshtml 檔案也包含能夠為每個客戶連絡人建立刪除按鈕的標記:The Index.cshtml file also contains markup to create a delete button for each customer contact:

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

使用 HTML 轉譯刪除按鈕時,其 formaction 會包含下列項目的參數:When the delete button is rendered in HTML, its formaction includes parameters for:

  • asp-route-id 屬性指定的客戶連絡人識別碼。The customer contact ID specified by the asp-route-id attribute.
  • asp-page-handler 屬性指定的 handlerThe handler specified by the asp-page-handler attribute.

以下是轉譯的刪除按鈕範例,內含客戶連絡人識別碼 1Here is an example of a rendered delete button with a customer contact ID of 1:

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

選取按鈕時,表單 POST 要求會傳送至伺服器。When the button is selected, a form POST request is sent to the server. 依照慣例,會依據配置 OnPost[handler]Async,按 handler 參數的值來選取處理常式方法。By convention, the name of the handler method is selected based on the value of the handler parameter according to the scheme OnPost[handler]Async.

在此範例中,因為 handlerdelete,所以會使用 OnPostDeleteAsync 處理常式方法來處理 POST 要求。Because the handler is delete in this example, the OnPostDeleteAsync handler method is used to process the POST request. asp-page-handler 設為其他值 (例如 remove),則會選取名為 OnPostRemoveAsync 的頁面處理常式。If the asp-page-handler is set to a different value, such as remove, a page handler method with the name OnPostRemoveAsync is selected.

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

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

    return RedirectToPage();
}

OnPostDeleteAsync 方法:The OnPostDeleteAsync method:

  • 接受查詢字串的 idAccepts the id from the query string.
  • 使用 FindAsync 在資料庫中查詢客戶連絡人。Queries the database for the customer contact with FindAsync.
  • 若找到客戶連絡人,會從客戶連絡人清單中予以移除。If the customer contact is found, they're removed from the list of customer contacts. 資料庫隨即更新。The database is updated.
  • 呼叫 RedirectToPage 以重新導向至根索引頁 (/Index)。Calls RedirectToPage to redirect to the root Index page (/Index).

將頁面屬性標示為必要Mark page properties as required

PageModel 上的屬性可以裝飾以 Required 屬性:Properties on a PageModel can be decorated with the Required attribute:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        [Required(ErrorMessage = "Color is required")]
        public string Color { get; set; }

        public IActionResult OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            // Process color.

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

如需詳細資訊,請參閱模型驗證For more information, see Model validation.

使用 OnGet 處理常式管理 HEAD 要求Manage HEAD requests with the OnGet handler

HEAD 要求可讓您擷取特定資源的標頭。HEAD requests allow you to retrieve the headers for a specific resource. 不同於 GET 要求,HEAD 要求不會傳回回應主體。Unlike GET requests, HEAD requests don't return a response body.

一般來說,HEAD 要求會建立 HEAD 處理常式並加以呼叫:Ordinarily, a HEAD handler is created and called for HEAD requests:

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

如果沒有定義 HEAD 處理常式 (OnHead),Razor 頁面會轉而呼叫 ASP.NET Core 2.1 或更新版本中的 GET 頁面處理常式 (OnGet)。If no HEAD handler (OnHead) is defined, Razor Pages falls back to calling the GET page handler (OnGet) in ASP.NET Core 2.1 or later. 在 ASP.NET Core 2.1 和 2.2 中,此行為會發生於 Startup.Configure 中的 SetCompatibilityVersionIn ASP.NET Core 2.1 and 2.2, this behavior occurs with the SetCompatibilityVersion in Startup.Configure:

services.AddMvc()
    .SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);

預設範本會在 ASP.NET Core 2.1 和 2.2 中產生 SetCompatibilityVersion 呼叫。The default templates generate the SetCompatibilityVersion call in ASP.NET Core 2.1 and 2.2.

SetCompatibilityVersion 實際上是將 Razor 頁面選項 AllowMappingHeadRequestsToGetHandler 設為 trueSetCompatibilityVersion effectively sets the Razor Pages option AllowMappingHeadRequestsToGetHandler to true.

您可以明確選擇特定行為,而無須選擇 SetCompatibilityVersion 所有 2.1 的行為。Rather than opting into all 2.1 behaviors with SetCompatibilityVersion, you can explicitly opt-in to specific behaviors. 下列程式碼選擇將 HEAD 要求對應到 GET 處理常式。The following code opts into the mapping HEAD requests to the GET handler.

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        options.AllowMappingHeadRequestsToGetHandler = true;
    });

XSRF/CSRF 和 Razor PagesXSRF/CSRF and Razor Pages

您不必撰寫任何防偽驗證程式碼。You don't have to write any code for antiforgery validation. 防偽權杖的產生和驗證會自動包含在 Razor 頁面中。Antiforgery token generation and validation are automatically included in Razor Pages.

搭配 Razor Pages 使用版面配置、部分、範本和標記協助程式。Using Layouts, partials, templates, and Tag Helpers with Razor Pages

Pages 可搭配 Razor 檢視引擎的所有功能一起使用。Pages work with all the capabilities of the Razor view engine. 版面配置、部分、範本、標記協助程式、_ViewStart.cshtml_ViewImports.cshtml 運作方式一如它們在傳統 Razor 檢視中的方式。Layouts, partials, templates, Tag Helpers, _ViewStart.cshtml, _ViewImports.cshtml work in the same way they do for conventional Razor views.

可利用這些功能的一部分來整理這個頁面。Let's declutter this page by taking advantage of some of those capabilities.

版面配置頁面新增至 Pages/Shared/_Layout.cshtmlAdd a layout page to Pages/Shared/_Layout.cshtml:

版面配置頁面新增至 Pages/_Layout.cshtmlAdd a layout page to Pages/_Layout.cshtml:

<!DOCTYPE html>
<html>
<head> 
    <title>Razor Pages Sample</title>      
</head>
<body>    
   <a asp-page="/Index">Home</a>
    @RenderBody()  
    <a asp-page="/Customers/Create">Create</a> <br />
</body>
</html>

配置The Layout:

  • 控制每個頁面的版面配置 (除非頁面退出版面配置)。Controls the layout of each page (unless the page opts out of layout).
  • 匯入 HTML 結構,例如 JavaScript 和樣式表。Imports HTML structures such as JavaScript and stylesheets.

如需詳細資訊,請參閱版面配置頁面See layout page for more information.

版面配置屬性是在 Pages/_ViewStart.cshtml 中設定:The Layout property is set in Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

版面配置位於 Pages/Shared 資料夾。The layout is in the Pages/Shared folder. 頁面會以階層方式尋找其他檢視 (版面配置、範本、部分),從目前頁面的相同資料夾開始。Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. 您可以從任何 Razor 頁面中的 Pages 資料夾下,使用 Pages/Shared 資料夾中的版面配置。A layout in the Pages/Shared folder can be used from any Razor page under the Pages folder.

版面配置頁面應位於 Pages/Shared 資料夾中。The layout file should go in the Pages/Shared folder.

此配置位於 Pages 資料夾。The layout is in the Pages folder. 頁面會以階層方式尋找其他檢視 (版面配置、範本、部分),從目前頁面的相同資料夾開始。Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. 您可以從任何 Razor 頁面下的 Pages 資料夾中,使用 Pages 資料夾中的版面配置。A layout in the Pages folder can be used from any Razor page under the Pages folder.

我們建議您將配置檔案放入 Views/Shared 資料夾。We recommend you not put the layout file in the Views/Shared folder. Views/Shared 是 MVC 檢視模式。Views/Shared is an MVC views pattern. Razor 頁面應該要依賴資料夾階層,不是路徑慣例。Razor Pages are meant to rely on folder hierarchy, not path conventions.

Razor 頁面的檢視搜尋包括 Pages 資料夾。View search from a Razor Page includes the Pages folder. 搭配 MVC 控制器使用的版面配置、範本和部分以及傳統的 Razor 檢視「就這麼簡單」。The layouts, templates, and partials you're using with MVC controllers and conventional Razor views just work.

新增 Pages/_ViewImports.cshtml 檔案:Add a Pages/_ViewImports.cshtml file:

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

本教學課程稍後會說明 @namespace@namespace is explained later in the tutorial. @addTagHelper 指示詞會將內建標記協助程式帶入 Pages 資料夾中的所有頁面。The @addTagHelper directive brings in the built-in Tag Helpers to all the pages in the Pages folder.

在頁面上明確使用 @namespace 指示詞時:When the @namespace directive is used explicitly on a page:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

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

指示詞會設定頁面的命名空間。The directive sets the namespace for the page. @model 指示詞不需要包含命名空間。The @model directive doesn't need to include the namespace.

@namespace 指示詞包含在 _ViewImports.cshtml 中時,指定的命名空間會在匯入 @namespace 指示詞的頁面中提供所產生之命名空間的前置詞。When the @namespace directive is contained in _ViewImports.cshtml, the specified namespace supplies the prefix for the generated namespace in the Page that imports the @namespace directive. 所產生命名空間的其餘部分 (後置字元部分) 是包含 _ViewImports.cshtml 的資料夾和包含頁面的資料夾之間,以句點分隔的相對路徑。The rest of the generated namespace (the suffix portion) is the dot-separated relative path between the folder containing _ViewImports.cshtml and the folder containing the page.

例如,PageModel 類別 Pages/Customers/Edit.cshtml.cs 會明確地設定命名空間:For example, the PageModel class Pages/Customers/Edit.cshtml.cs explicitly sets the namespace:

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

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

        // Code removed for brevity.

Pages/_ViewImports.cshtml 檔案會設定下列命名空間:The Pages/_ViewImports.cshtml file sets the following namespace:

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

Pages/Customers/Edit.cshtml Razor 頁面產生的命名空間和 PageModel 類別相同。The generated namespace for the Pages/Customers/Edit.cshtml Razor Page is the same as the PageModel class.

@namespace 也適用於傳統的 Razor 檢視。@namespace also works with conventional Razor views.

原始的 Pages/Create.cshtml 檢視檔案:The original Pages/Create.cshtml view file:

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

<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" />
    </form>
</body>
</html>

更新的 Pages/Create.cshtml 檢視檔案:The updated Pages/Create.cshtml view file:

@page
@model CreateModel

<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" />
    </form>
</body>
</html>

Razor Pages 入門專案包含 Pages/_ValidationScriptsPartial.cshtml,連結用戶端驗證。The Razor Pages starter project contains the Pages/_ValidationScriptsPartial.cshtml, which hooks up client-side validation.

如需部分檢視的詳細資訊,請參閱 ASP.NET Core 中的部分檢視For more information on partial views, see ASP.NET Core 中的部分檢視.

產生頁面 URLURL generation for Pages

前面出現過的 Create 頁面使用 RedirectToPageThe Create page, shown previously, uses RedirectToPage:

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

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

應用程式有下列檔案/資料夾結構:The app has the following file/folder structure:

  • /Pages/Pages

    • Index.cshtmlIndex.cshtml

    • /Customers/Customers

      • Create.cshtmlCreate.cshtml
      • Edit.cshtmlEdit.cshtml
      • Index.cshtmlIndex.cshtml

Pages/Customers/Create.cshtmlPages/Customers/Edit.cshtml 頁面在成功後會重新導向至 Pages/Index.cshtmlThe Pages/Customers/Create.cshtml and Pages/Customers/Edit.cshtml pages redirect to Pages/Index.cshtml after success. 字串 /Index 為 URI 的一部分,可存取前一個頁面。The string /Index is part of the URI to access the preceding page. 字串 /Index 可以用來產生 Pages/Index.cshtml 頁面的 URI。The string /Index can be used to generate URIs to the Pages/Index.cshtml page. 例如:For example:

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

頁面名稱是從根 /Pages 資料夾到該頁面的路徑 (包括前置的 /,例如 /Index)。The page name is the path to the page from the root /Pages folder including a leading / (for example, /Index). 上述 URL 產生範例,透過硬式編碼的 URL 提供更加優異的選項與功能。The preceding URL generation samples offer enhanced options and functional capabilities over hardcoding a URL. URL 產生使用路由,可以根據路由在目的地路徑中定義的方式,產生並且編碼參數。URL generation uses routing and can generate and encode parameters according to how the route is defined in the destination path.

產生頁面 URL 支援相關的名稱。URL generation for pages supports relative names. 下表顯示從 Pages/Customers/Create.cshtml 以不同的 RedirectToPage 參數選取的索引頁:The following table shows which Index page is selected with different RedirectToPage parameters from Pages/Customers/Create.cshtml:

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

RedirectToPage("Index")RedirectToPage("./Index")RedirectToPage("../Index") 是「相對名稱」。RedirectToPage("Index"), RedirectToPage("./Index"), and RedirectToPage("../Index") are relative names. RedirectToPage 參數「結合」了目前頁面的路徑,以計算目的地頁面的名稱。The RedirectToPage parameter is combined with the path of the current page to compute the name of the destination page.

相對名稱連結在以複雜結構建置網站時很有用。Relative name linking is useful when building sites with a complex structure. 如果您使用相對名稱連結資料夾中的頁面,您可以重新命名該資料夾。If you use relative names to link between pages in a folder, you can rename that folder. 所有連結仍可運作 (因為它們不包含資料夾名稱)。All the links still work (because they didn't include the folder name).

若要重新導向到不同區域中的頁面,請指定區域:To redirect to a page in a different Area, specify the area:

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

如需詳細資訊,請參閱ASP.NET Core 中的區域For more information, see ASP.NET Core 中的區域.

ViewData 屬性ViewData attribute

資料可以傳遞至具有 ViewDataAttribute 的頁面。Data can be passed to a page with ViewDataAttribute. 控制器或 Razor 頁面模型上裝飾以 [ViewData] 的屬性會將其值儲存在 ViewDataDictionary 並從中載入。Properties on controllers or Razor Page models decorated with [ViewData] have their values stored and loaded from the ViewDataDictionary.

在下列範例中,AboutModel 包含裝飾以 [ViewData]Title屬性。In the following example, the AboutModel contains a Title property decorated with [ViewData]. Title 屬性會設定為 [關於] 頁面的標題:The Title property is set to the title of the About page:

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

    public void OnGet()
    {
    }
}

在 [關於] 頁面上,存取 Title 屬性作為模型屬性:In the About page, access the Title property as a model property:

<h1>@Model.Title</h1>

在此配置中,標題會從 ViewData 字典中讀取:In the layout, the title is read from the ViewData dictionary:

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

TempDataTempData

ASP.NET Core 公開控制器上的 TempData 屬性。ASP.NET Core exposes the TempData property on a controller. 這個屬性會儲存資料,直到讀取為止。This property stores data until it's read. KeepPeek 方法可以用來檢查資料,不用刪除。The Keep and Peek methods can be used to examine the data without deletion. 當有多個要求需要資料時,TempData 對重新導向很有幫助。TempData is useful for redirection, when data is needed for more than a single request.

[TempData] 是 ASP.NET Core 2.0 的新屬性,在控制站和頁面都受支援。The [TempData] attribute is new in ASP.NET Core 2.0 and is supported on controllers and pages.

下列程式碼會設定使用 TempDataMessage 值:The following code sets the value of Message using TempData:

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 檔案中的下列標記會顯示使用 TempDataMessage 值。The following markup in the Pages/Customers/Index.cshtml file displays the value of Message using TempData.

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

Pages/Customers/Index.cshtml.cs 頁面模型會將 [TempData] 屬性 (attribute) 套用到 Message 屬性 (property)。The Pages/Customers/Index.cshtml.cs page model applies the [TempData] attribute to the Message property.

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

如需詳細資訊,請參閱 TempDataFor more information, see TempData .

每頁面有多個處理常式Multiple handlers per page

下列頁面會使用 asp-page-handler 標記協助程式為兩個頁面處理常式產生標記:The following page generates markup for two page handlers using the asp-page-handler Tag Helper:

@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>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

上例中的表單有兩個提交按鈕,每一個都使用 FormActionTagHelper 提交至不同的 URL。The form in the preceding example has two submit buttons, each using the FormActionTagHelper to submit to a different URL. asp-page-handler 屬性附隨於 asp-pageThe asp-page-handler attribute is a companion to asp-page. asp-page-handler 產生的 URL 會提交至頁面所定義的每一個處理常式方法。asp-page-handler generates URLs that submit to each of the handler methods defined by a page. 因為範例連結至目前的頁面,所以未指定 asp-pageasp-page isn't specified because the sample is linking to the current page.

頁面模型:The page model:

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?.ToUpper();
            return await OnPostJoinListAsync();
        }
    }
}

上述程式碼使用「具名的處理常式方法」。The preceding code uses named handler methods. 具名的處理常式方法的建立方式是採用名稱中在 On<HTTP Verb> 後面、Async 之前 (如有) 的文字。Named handler methods are created by taking the text in the name after On<HTTP Verb> and before Async (if present). 在上例中,頁面方法是 OnPostJoinListAsync 和 OnPostJoinListUCAsync。In the preceding example, the page methods are OnPostJoinListAsync and OnPostJoinListUCAsync. 移除 OnPostAsync,處理常式名稱就是 JoinListJoinListUCWith OnPost and Async removed, the handler names are JoinList and JoinListUC.

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

使用上述程式碼,提交至 OnPostJoinListAsync 的 URL 路徑是 http://localhost:5000/Customers/CreateFATH?handler=JoinListUsing the preceding code, the URL path that submits to OnPostJoinListAsync is http://localhost:5000/Customers/CreateFATH?handler=JoinList. 提交至 OnPostJoinListUCAsync 的 URL 路徑是 http://localhost:5000/Customers/CreateFATH?handler=JoinListUCThe URL path that submits to OnPostJoinListUCAsync is http://localhost:5000/Customers/CreateFATH?handler=JoinListUC.

自訂路由Custom routes

使用 @page 指示詞,可以:Use the @page directive to:

  • 指定頁面的自訂路由。Specify a custom route to a page. 例如,[關於] 頁面的路由可使用 @page "/Some/Other/Path" 設為 /Some/Other/PathFor example, the route to the About page can be set to /Some/Other/Path with @page "/Some/Other/Path".
  • 將區段附加到頁面的預設路由。Append segments to a page's default route. 例如,使用 @page "item" 可將 "item" 區段新增到頁面的預設路由。For example, an "item" segment can be added to a page's default route with @page "item".
  • 將參數附加到頁面的預設路由。Append parameters to a page's default route. 例如,具有 @page "{id}" 的頁面可要求識別碼參數 idFor example, an ID parameter, id, can be required for a page with @page "{id}".

支援在路徑開頭以波狀符號 (~) 指定根相對路徑。A root-relative path designated by a tilde (~) at the beginning of the path is supported. 例如,@page "~/Some/Other/Path"@page "/Some/Other/Path" 相同。For example, @page "~/Some/Other/Path" is the same as @page "/Some/Other/Path".

您可以藉由指定路由範本 @page "{handler?}",將 URL 中的查詢字串 ?handler=JoinList 變更為路由區段 /JoinListYou can change the query string ?handler=JoinList in the URL to a route segment /JoinList by specifying the route template @page "{handler?}".

如果您不喜歡 URL 有查詢字串 ?handler=JoinList,您可以變更路由,將處理常式名稱置於 URL 的路徑部分。If you don't like the query string ?handler=JoinList in the URL, you can change the route to put the handler name in the path portion of the URL. 您可以新增路由範本,在 @page 指示詞後面用雙引號括住,以自訂路由。You can customize the route by adding a route template enclosed in double quotes after the @page directive.

@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 路徑是 http://localhost:5000/Customers/CreateFATH/JoinListUsing the preceding code, the URL path that submits to OnPostJoinListAsync is http://localhost:5000/Customers/CreateFATH/JoinList. 提交至 OnPostJoinListUCAsync 的 URL 路徑是 http://localhost:5000/Customers/CreateFATH/JoinListUCThe URL path that submits to OnPostJoinListUCAsync is http://localhost:5000/Customers/CreateFATH/JoinListUC.

跟在 handler 後面的 ? 表示路由參數為選擇性。The ? following handler means the route parameter is optional.

組態與設定Configuration and settings

若要設定進階選項,請在 MVC 產生器上使用擴充方法 AddRazorPagesOptionsTo configure advanced options, use the extension method AddRazorPagesOptions on the MVC builder:

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

目前可以使用 RazorPagesOptions 設定頁面的根目錄,或新增頁面的應用程式模型慣例。Currently you can use the RazorPagesOptions to set the root directory for pages, or add application model conventions for pages. 我們將在未來以這種方式獲得更多的擴充性。We'll enable more extensibility this way in the future.

若要先行編譯檢視,請參閱 Razor 檢視編譯To precompile views, see Razor view compilation .

下載或檢視範例程式碼Download or view sample code.

請參閱根據本簡介編纂的開始使用 Razor PagesSee Get started with Razor Pages, which builds on this introduction.

指定 Razor Pages 位於內容根目錄Specify that Razor Pages are at the content root

根據預設,Razor Pages 位於 /Pages 根目錄。By default, Razor Pages are rooted in the /Pages directory. WithRazorPagesAtContentRoot 新增至 AddMvc 可指定 Razor Pages 位於應用程式的內容根目錄 (ContentRootPath):Add WithRazorPagesAtContentRoot to AddMvc to specify that your Razor Pages are at the content root (ContentRootPath) of the app:

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesAtContentRoot();

指定 Razor Pages 位於自訂根目錄Specify that Razor Pages are at a custom root directory

WithRazorPagesRoot 新增至 AddMvc 可指定 Razor Pages 位於應用程式的自訂根目錄 (提供相對路徑):Add WithRazorPagesRoot to AddMvc to specify that your Razor Pages are at a custom root directory in the app (provide a relative path):

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesRoot("/path/to/razor/pages");

其他資源Additional resources