路由是 ASP.NET Core MVC 的靈魂,將 HTTP 請求映射到控制器動作。本文從零建立專案,逐步講解傳統路由、預設值、靜態片段、自訂參數、約束條件、命名空間消歧義、HTTP 動詞限制等核心概念,並提供可直接複製執行的程式碼範例。
dotnet new mvc -n RoutingDemo -o RoutingDemo
cd RoutingDemo
# 刪除 Program.cs / Startup.cs 中預設路由,手工建構以利學習
// Program.cs (或 Startup.cs ConfigureServices)
builder.Services.AddControllersWithViews();
var app = builder.Build();
// 傳統路由註冊 (順序決定優先權,越詳細越優先)
app.MapControllerRoute(
name: "basic",
pattern: "Home/Index", // 靜態 URL 片段
defaults: new { controller = "Home", action = "Index" });
app.Run();
瀏覽 /Home/Index → 呼叫 HomeController.Index()。
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}", // {id?} 表示選填
defaults: new { controller = "Home", action = "Index" });
{controller=Home}:參數名稱必須與 URL 佔位符同名 (大小寫不敏感)。id?:問號表示選填,若無提供則為 null。現在 /、/Home、/Home/Index、/Home/Index/123 皆可正確對應。
app.MapControllerRoute(
name: "pretty",
pattern: "X{controller}-{action}", // 靜態字元 'X' 與 '-' 直接出現在 URL
defaults: new { controller = "Home", action = "Index" });
對應 URL:/XHome-Index。移除 X 則無法比對 (除非另有路由允許)。
app.MapControllerRoute(
name: "custom",
pattern: "{controller}/{action}/{id}/{*catchall}",
defaults: new { controller = "Home", action = "Index" });
{id}:必要參數,對應 Action 參數 int id 或 string id。{*catchall}:捕獲剩餘所有片段 (含斜線),對應 string catchall。public class HomeController : Controller
{
public IActionResult Index(int? id, string catchall)
{
ViewBag.Id = id;
ViewBag.Catchall = catchall;
return View();
}
}
@* Views/Home/Index.cshtml *@
Id: @ViewBag.Id
Catchall: @ViewBag.Catchall
瀏覽 /Home/Index/42/a/b/c → Id=42, Catchall="a/b/c"。
若專案與引用的類別庫皆有 HomeController,路由無法決定使用哪個。
app.MapControllerRoute(
name: "namespaced",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { }, // 空約束
dataTokens: new { namespaces = new[] { "RoutingDemo.Controllers" } });
dataTokens.namespace 限定路由僅掃描指定命名空間,徹底解決衝突。
| 約束 | 說明 | 範例 |
|---|---|---|
int | 32 位元整數 | {id:int} |
bool | true/false | {flag:bool} |
datetime | DateTime 格式 | {date:datetime} |
decimal | Decimal | {price:decimal} |
guid | GUID | {token:guid} |
length(min,max) | 字串長度範圍 | {name:length(3,20)} |
min/max/range | 數值範圍 | {age:range(18,65)} |
alpha | 純字母 | {code:alpha} |
regex(pattern) | 正則表達式 | {slug:regex(^[a-z0-9-]+$)} |
httpmethod | 限定 HTTP 動詞 | httpmethod=GET,POST |
app.MapControllerRoute(
name: "constrained",
pattern: "api/products/{id:int:min(1)}",
defaults: new { controller = "Products", action = "Detail" });
現在 /api/products/abc 將 404,/api/products/10 正常。
需求:僅允許 Chrome 瀏覽器存取特定路由。
// Constraints/ChromeOnlyConstraint.cs
public class ChromeOnlyConstraint : IRouteConstraint
{
public bool Match(HttpContext httpContext, IRouter route, string routeKey,
RouteValueDictionary values, RouteDirection routeDirection)
{
var ua = httpContext.Request.Headers["User-Agent"].ToString();
return ua.Contains("Chrome") && !ua.Contains("Edg"); // 排除 Edge
}
}
// 註冊路由
app.MapControllerRoute(
name: "chromeOnly",
pattern: "chrome-only/{action}",
defaults: new { controller = "Home" },
constraints: new { browser = new ChromeOnlyConstraint() });
注意:User-Agent 可偽造,僅供教學示範,生產環境請用功能偵測或伺服端邏輯。
在 Controller/Action 直接標註路由,集中管理、直觀、支援 OpenAPI/Swagger 自動生成。
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet("{id:int}")] // GET /api/products/123
public IActionResult Get(int id) => Ok(new { Id = id });
[HttpPost] // POST /api/products
public IActionResult Create(ProductDto dto) => Ok(dto);
[HttpPut("{id:int}")] // PUT /api/products/123
public IActionResult Update(int id, ProductDto dto) => Ok();
[HttpDelete("{id:int}")] // DELETE /api/products/123
public IActionResult Delete(int id) => NoContent();
}
啟用:builder.Services.AddControllers(); app.MapControllers(); (取代傳統路由)。
MapControllerRoute 呼叫順序:先註冊優先。{*catchall}。options.LowercaseUrls = true 強制小寫)。app.UseDeveloperExceptionPage(),錯誤頁會顯示匹配路由詳情。app.Use(async (ctx, next) => { var endpoint = ctx.GetEndpoint(); logger.LogInfo($"{ctx.Request.Path} -> {endpoint?.DisplayName}"); await next(); });dotnet-route 全域工具:dotnet tool install -g dotnet-route && dotnet-route 列出所有已註冊路由。ASP.NET Core 路由系統靈活強大:傳統路由適合伺服端渲染 MVC、集中管理;屬性路由適合 Web API、RESTful 設計、Swagger 整合。掌握「預設值、選填參數、靜態片段、Catch-all、命名空間消歧義、內建/自訂約束、HTTP 動詞限制」七大核心技巧,即可駕馭 99% 路由需求。