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

路由系統 Routing

路由是什麼?

路由就像是郵差送信——用戶發出請求(寄信),路由系統根據 URL(地址)找到對應的 Controller Action(收件人)。

用戶請求 GET /Products/Details/5
        ↓
路由系統比對 {controller}/{action}/{id?}
        ↓
找到 ProductsController.Details(5)
        ↓
回傳結果

傳統路由 Conventional Routing

Program.cs 中設定路由模板:

// Program.cs - 設定傳統路由
app.MapControllerRoute(
    name: "default",                          // 路由名稱
    pattern: "{controller=Home}/{action=Index}/{id?}" // 路由模板
);
// controller=Home → 預設控制器為 Home
// action=Index   → 預設動作為 Index
// id?            → id 是可選參數

路由比對範例

URL Controller Action id
/ Home Index null
/Products Products Index null
/Products/Details Products Details null
/Products/Details/5 Products Details 5

屬性路由 Attribute Routing

直接在 Controller 或 Action 上標註路由:

// 使用屬性路由的控制器
[Route("api/[controller]")]  // 基底路由,[controller] 會自動替換為類別名稱
public class ProductsController : Controller
{
    [Route("")]              // 對應 GET /api/Products
    [Route("list")]          // 也對應 GET /api/Products/list
    public IActionResult Index()
    {
        return View();       // 回傳視圖
    }

    [Route("{id:int}")]      // 對應 GET /api/Products/5,id 必須是整數
    public IActionResult Details(int id)
    {
        return Content($"商品 ID:{id}"); // 回傳文字內容
    }
}

路由參數與限制條件

// 路由限制條件範例
public class CatalogController : Controller
{
    // {id:int} → id 必須是整數
    [HttpGet("catalog/{id:int}")]
    public IActionResult ById(int id)
    {
        return Content($"用 ID 查詢:{id}"); // 整數 ID 查詢
    }

    // {name:alpha} → name 只能是英文字母
    [HttpGet("catalog/{name:alpha}")]
    public IActionResult ByName(string name)
    {
        return Content($"用名稱查詢:{name}"); // 名稱查詢
    }

    // {slug:regex(^[a-z0-9-]+$)} → 自訂正規表示式
    [HttpGet("catalog/slug/{slug:regex(^[[a-z0-9-]]+$)}")]
    public IActionResult BySlug(string slug)
    {
        return Content($"用 Slug 查詢:{slug}"); // Slug 查詢
    }

    // 可選參數用 ? 表示
    [HttpGet("catalog/page/{page:int?}")]
    public IActionResult List(int page = 1)
    {
        return Content($"第 {page} 頁"); // 預設為第 1 頁
    }
}

常見路由限制條件

限制條件 說明 範例
{id:int} 整數 123
{name:alpha} 英文字母 hello
{price:decimal} 十進位數 9.99
{flag:bool} 布林值 true
{id:min(1)} 最小值 1 1, 100
{name:maxlength(20)} 最大長度 20 short

多重路由模式

// Program.cs - 設定多組路由
// 第一組:管理後台路由
app.MapControllerRoute(
    name: "admin",                             // 路由名稱
    pattern: "admin/{controller=Dashboard}/{action=Index}/{id?}" // 後台路由
);

// 第二組:預設路由
app.MapControllerRoute(
    name: "default",                           // 路由名稱
    pattern: "{controller=Home}/{action=Index}/{id?}" // 預設路由
);

Area 路由

Area(區域)用來將大型專案分組:

// Areas/Admin/Controllers/DashboardController.cs
[Area("Admin")]                      // 標記屬於 Admin 區域
public class DashboardController : Controller
{
    public IActionResult Index()
    {
        return View();               // 回傳 Areas/Admin/Views/Dashboard/Index.cshtml
    }
}
// Program.cs - 設定 Area 路由
app.MapControllerRoute(
    name: "areas",                              // 路由名稱
    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}" // Area 路由模板
);
// area:exists → 確認 Area 存在才比對

🤔 我這樣寫為什麼會錯?

❌ 錯誤 1:路由模板重複衝突

// ❌ 兩個 Action 路由一模一樣,系統不知道要用哪個
[HttpGet("products/{id}")]
public IActionResult GetById(int id) => Content("By ID");

[HttpGet("products/{name}")]
public IActionResult GetByName(string name) => Content("By Name");
// ✅ 用限制條件區分
[HttpGet("products/{id:int}")]           // id 必須是整數
public IActionResult GetById(int id) => Content("By ID");

[HttpGet("products/{name:alpha}")]       // name 必須是字母
public IActionResult GetByName(string name) => Content("By Name");

為什麼? 沒有限制條件,products/5 同時符合兩個路由,會造成 AmbiguousMatchException。加上 :int:alpha 就能明確區分。

❌ 錯誤 2:忘記在 Area Controller 加 [Area] 屬性

// ❌ 少了 [Area] 屬性,路由找不到
public class AdminDashboardController : Controller
{
    public IActionResult Index() => View();
}
// ✅ 加上 [Area("Admin")] 屬性
[Area("Admin")]                          // 標記屬於 Admin 區域
public class AdminDashboardController : Controller
{
    public IActionResult Index() => View(); // 正確對應 Area 路由
}

為什麼? Area 路由需要 [Area] 屬性來比對 {area:exists},沒標記就無法匹配路由。

❌ 錯誤 3:路由順序錯誤

// ❌ 萬用路由放在前面,後面的特定路由永遠不會被匹配
app.MapControllerRoute("catchall", "{*url}", new { controller="Home", action="NotFound" });
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
// ✅ 特定路由放前面,萬用路由放最後
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute("catchall", "{*url}", new { controller="Home", action="NotFound" });

為什麼? 路由是依序比對的,萬用路由 {*url} 會匹配所有 URL,放在前面就會把所有請求攔截掉。

💡 大家的想法 · 0

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