☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
ASP.NET Core 初學

📑 Razor Pages

📌 什麼是 Razor Pages?

Razor Pages 是 ASP.NET Core 提供的一種以頁面為中心的開發模式,比 MVC 更簡單直觀。

想像你在蓋房子:

  • MVC 模式 像是找三個不同的工匠——一個畫設計圖(View)、一個管工地(Controller)、一個準備材料(Model),分工合作
  • Razor Pages 像是一個全能工匠——每一面牆(頁面)都由一個人負責設計和施工,簡單的案子效率更高

Razor Pages 特別適合以頁面為主的網站(如部落格、表單頁面、報表頁面),不需要 Controller 的額外層級。


🏗️ Razor Pages 基本結構

Pages/                          # 所有頁面都放在 Pages 資料夾
├── Index.cshtml               # 首頁的 HTML 模板(像牆壁的外觀)
├── Index.cshtml.cs            # 首頁的邏輯程式碼(像牆壁的內部結構)
├── About.cshtml               # 關於頁面
├── About.cshtml.cs            # 關於頁面的邏輯
├── Products/                  # 子資料夾 = URL 路徑的一部分
│   ├── Index.cshtml           # /Products 頁面
│   ├── Details.cshtml         # /Products/Details 頁面
│   └── Create.cshtml          # /Products/Create 頁面
└── Shared/                    # 共用元件
    └── _Layout.cshtml         # 版面配置(像房子的骨架)

📄 PageModel 類別

// Pages/Products/Index.cshtml.cs
using Microsoft.AspNetCore.Mvc;           // 引用 MVC 命名空間
using Microsoft.AspNetCore.Mvc.RazorPages; // 引用 Razor Pages 命名空間

public class IndexModel : PageModel // 繼承 PageModel 基底類別
{
    // 屬性:提供資料給頁面(像準備好的食材放在工作台上)
    public List<Product> Products { get; set; } = new(); // 商品清單

    public string Message { get; set; } = ""; // 顯示訊息

    // OnGet:處理 GET 請求(使用者進入頁面時執行)
    public void OnGet() // 當使用者瀏覽此頁面時
    {
        Message = "歡迎來到商品列表!"; // 設定訊息
        Products = new List<Product>       // 準備商品資料
        {
            new Product { Id = 1, Name = "筆電", Price = 30000 },  // 第一個商品
            new Product { Id = 2, Name = "手機", Price = 25000 },  // 第二個商品
            new Product { Id = 3, Name = "平板", Price = 18000 }   // 第三個商品
        };
    }

    // OnPost:處理 POST 請求(使用者提交表單時執行)
    public IActionResult OnPost() // 當使用者送出表單時
    {
        if (!ModelState.IsValid) // 如果資料驗證失敗
        {
            return Page(); // 回到同一頁,顯示錯誤訊息
        }
        // 處理表單資料...
        return RedirectToPage("./Index"); // 重新導向到清單頁面
    }
}

🎨 Razor 頁面模板 (.cshtml)

@* Pages/Products/Index.cshtml *@
@page                                    @* 這行超重要!標記此檔案是 Razor Page *@
@model IndexModel                        @* 綁定對應的 PageModel 類別 *@

<h1>@Model.Message</h1>                @* 顯示 PageModel 中的 Message 屬性 *@

@* 用表格顯示商品清單 *@
<table class="table">
    <thead>
        <tr>
            <th>ID</th>
            <th>商品名稱</th>
            <th>價格</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var product in Model.Products) @* 走訪每個商品 *@
        {
            <tr>
                <td>@product.Id</td>         @* 顯示商品 ID *@
                <td>@product.Name</td>       @* 顯示商品名稱 *@
                <td>@product.Price 元</td>   @* 顯示商品價格 *@
                <td>
                    @* asp-page 指向另一個 Razor Page *@
                    <a asp-page="./Details"
                       asp-route-id="@product.Id">
                        詳細資料
                    </a>
                </td>
            </tr>
        }
    </tbody>
</table>

@* 新增商品的表單 *@
<form method="post">                    @* POST 表單會觸發 OnPost 方法 *@
    <div>
        <label>商品名稱</label>
        <input asp-for="NewProduct.Name" /> @* 綁定到 PageModel 的屬性 *@
    </div>
    <button type="submit">新增</button>  @* 送出按鈕 *@
</form>

🔄 OnGet 與 OnPost 處理器

// Pages/Contact.cshtml.cs - 聯絡表單範例
public class ContactModel : PageModel // 聯絡頁面的 PageModel
{
    [BindProperty] // 自動將表單資料綁定到此屬性(像自動拆信取出內容)
    public ContactForm ContactForm { get; set; } = new(); // 表單資料

    public string SuccessMessage { get; set; } = ""; // 成功訊息

    // 處理 GET 請求:顯示空白表單
    public void OnGet() // 使用者開啟頁面時
    {
        // 不需要做什麼,顯示空白表單即可
    }

    // 處理 POST 請求:接收表單資料
    public IActionResult OnPost() // 使用者提交表單時
    {
        if (!ModelState.IsValid) // 驗證是否通過
        {
            return Page(); // 驗證失敗,回到同一頁顯示錯誤
        }

        // 處理表單資料(例如寄送 Email)
        SuccessMessage = $"感謝 {ContactForm.Name},我們已收到您的訊息!"; // 設定成功訊息
        return Page(); // 回到同一頁顯示成功訊息
    }

    // 也可以有多個 POST Handler(像一個頁面有多個按鈕)
    public IActionResult OnPostDelete(int id) // 當按下「刪除」按鈕時
    {
        // 刪除指定的資料
        Console.WriteLine($"刪除 ID:{id}"); // 記錄刪除操作
        return RedirectToPage(); // 重新導向回同一頁
    }

    // 非同步版本
    public async Task<IActionResult> OnPostAsync() // 非同步的 POST 處理器
    {
        if (!ModelState.IsValid) // 驗證
            return Page(); // 失敗就回到頁面

        await SaveToDatabase(ContactForm); // 非同步存入資料庫
        return RedirectToPage("./ThankYou"); // 導向感謝頁面
    }
}

🏷️ @page 指令與 asp-page Tag Helper

// @page 指令可以自訂路由模板
// Pages/Products/Details.cshtml
@page "{id:int}"    // URL 為 /Products/Details/5,id 必須是整數
@model DetailsModel  // 綁定 PageModel
// Pages/Products/Details.cshtml.cs
public class DetailsModel : PageModel // 商品詳情的 PageModel
{
    public Product Product { get; set; } = new(); // 商品資料

    public void OnGet(int id) // id 會自動從路由參數取得
    {
        // 根據 id 查詢商品
        Product = GetProductById(id); // 從資料庫取得商品
    }
}
@* 使用 asp-page 在頁面之間導航(像建築物裡的門,連接不同房間) *@

@* 連結到同一層的頁面 *@
<a asp-page="./Create">新增商品</a>          @* 連到 /Products/Create *@

@* 帶路由參數的連結 *@
<a asp-page="./Details"
   asp-route-id="5">查看商品 5</a>            @* 連到 /Products/Details/5 *@

@* 連結到根層的頁面 *@
<a asp-page="/Index">回首頁</a>              @* 連到 / *@

@* 帶查詢字串的連結 *@
<a asp-page="./Index"
   asp-route-search="手機">搜尋手機</a>       @* 連到 /Products?search=手機 *@

📊 Razor Pages vs MVC:何時用哪個?

情境 推薦方式 原因
簡單的表單頁面 Razor Pages 一個檔案搞定,不需要 Controller
部落格或內容網站 Razor Pages 以頁面為中心,結構直觀
複雜的 Web API MVC (Controller) Controller 更適合 API 設計
大型企業應用 MVC 更好的關注點分離
學習 ASP.NET Razor Pages 入門更簡單
CRUD 表單 Razor Pages 內建 Handler 模式很適合

🤔 我這樣寫為什麼會錯?

❌ 錯誤 1:忘記加 @page 指令

@* ❌ 錯誤寫法:忘記 @page 指令 *@
@model IndexModel   @* 只寫了 model,沒有 @page *@

<h1>我的頁面</h1>
@* 這個頁面無法被路由到!因為缺少 @page 指令 *@
@* ASP.NET 會把它當成一般的 View,而不是 Razor Page *@
@* ✅ 正確寫法:第一行就要有 @page *@
@page                @* 告訴 ASP.NET 這是一個 Razor Page *@
@model IndexModel    @* 綁定 PageModel *@

<h1>我的頁面</h1>
@* 現在可以透過 URL 正確存取了 *@

解釋: @page 就像門牌號碼——沒有門牌,郵差(路由系統)就找不到你家(頁面)。忘記加 @page 是 Razor Pages 最常見的錯誤,頁面會變成一般的 View,只能被 Controller 引用,無法直接透過 URL 存取。

❌ 錯誤 2:沒有使用 [BindProperty]

// ❌ 錯誤寫法:POST 表單的資料沒有綁定
public class CreateModel : PageModel // 新增頁面
{
    public Product Product { get; set; } = new(); // 沒有加 [BindProperty]

    public IActionResult OnPost() // 接收表單
    {
        // Product 永遠是空的!因為沒有 [BindProperty]
        Console.WriteLine(Product.Name); // 永遠是 ""(空字串)
        return Page(); // 回到頁面
    }
}
// ✅ 正確寫法:用 [BindProperty] 標記要綁定的屬性
public class CreateModel : PageModel // 新增頁面
{
    [BindProperty] // 告訴 ASP.NET 自動將表單資料填入這個屬性
    public Product Product { get; set; } = new(); // 會自動接收表單資料

    public IActionResult OnPost() // 接收表單
    {
        // Product 已經自動填入表單的資料了
        Console.WriteLine(Product.Name); // 正確取得使用者輸入的名稱
        return Page(); // 回到頁面
    }
}

解釋: [BindProperty] 就像自動拆信機——沒有它,信(表單資料)送到了但沒人拆開來看,所以你的程式什麼都收不到。對於 POST 表單來說,[BindProperty] 是必要的。

❌ 錯誤 3:GET 請求用 [BindProperty] 卻沒設定 SupportsGet

// ❌ 錯誤寫法:想在 GET 請求中取得查詢字串參數
public class SearchModel : PageModel // 搜尋頁面
{
    [BindProperty] // 預設只在 POST 時綁定
    public string Keyword { get; set; } = ""; // GET 請求時永遠是空的

    public void OnGet() // GET /Search?Keyword=手機
    {
        // Keyword 仍然是空字串![BindProperty] 預設不支援 GET
    }
}
// ✅ 正確寫法:加上 SupportsGet = true
public class SearchModel : PageModel // 搜尋頁面
{
    [BindProperty(SupportsGet = true)] // 明確允許 GET 請求的繫結
    public string Keyword { get; set; } = ""; // 現在 GET 時也能取得了

    public void OnGet() // GET /Search?Keyword=手機
    {
        // Keyword = "手機",正確取得查詢字串參數!
    }
}

解釋: 預設情況下,[BindProperty] 只處理 POST 請求。如果你想在 GET 請求中也能自動綁定查詢字串參數,必須加上 SupportsGet = true。這是一個安全設計,避免 GET 請求意外修改了資料。

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊