在昨天的文章中,我們已經建立了基本的 Web API 專案,並理解了 API 的基本運作方式。今天要深入探討的是 Routing(路由)與 Controller(控制器) —— 這是 ASP.NET Core Web API 的靈魂所在。
Routing(路由)決定了使用者請求的 URL 該如何被導向到對應的 Controller 與 Action,簡單來說,當使用者呼叫 https://localhost:5001/api/products/1 時,框架會透過 Routing Table(路由表) 找到哪個控制器的哪個方法要被執行。
首先可以看到在 ASP.NET Core 使用 Endpoint Routing,在 Program.cs 中通常可以看到這樣的設定:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers(); // 啟用屬性路由
app.Run();
這行 app.MapControllers() 代表應用程式啟用 Attribute Routing(屬性路由),也就是透過 Controller 上的屬性 [Route()] 來定義 URL 規則。
建立一個最簡單的 Controller:
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "Keyboard", "Mouse", "Monitor" };
}
[HttpGet("{id}")]
public string Get(int id)
{
return $"Product ID: {id}";
}
}
當我們執行後,可以使用以下路徑呼叫:
方法 | 路徑 | 說明 |
---|---|---|
GET | /api/products |
取得所有產品 |
GET | /api/products/1 |
取得 ID=1 的產品 |
屬性路由是目前最常用的方式,它允許在控制器或方法上直接定義 URL 規則:
[Route("api/products")]
public class ProductsController : ControllerBase
{
[HttpGet] // GET api/products
public IEnumerable<Product> GetAll() => _repository.GetAll();
[HttpGet("{id}")] // GET api/products/5
public Product GetById(int id) => _repository.Get(id);
[HttpPost] // POST api/products
public void Create(Product product) => _repository.Add(product);
}
這樣可以讓路由與程式邏輯清楚地綁在一起,維護性高,也容易閱讀。
路由參數可以限制輸入格式,避免錯誤請求。舉例來說:
[HttpGet("{id:int}")]
public IActionResult GetById(int id) => Ok($"Product ID: {id}");
[HttpGet("{name:alpha}")]
public IActionResult GetByName(string name) => Ok($"Product Name: {name}"
範例路由 | 約束說明 |
---|---|
{id:int} |
必須為整數 |
{name:alpha} |
必須為字母組成 |
{price:decimal} |
必須為數字(含小數點) |
{date:datetime} |
必須是有效日期格式 |
有時候我們希望參數是可選的:
[HttpGet("{id?}")]
public IActionResult Get(int? id)
{
if (id == null)
return Ok("All Products");
else
return Ok($"Product {id}");
}
或者給定預設值:
[HttpGet("{category=general}")]
public IActionResult GetCategory(string category) => Ok($"Category: {category}");
屬性路由可以互相組合,控制器定義一個基本路由,方法再延伸:
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
[HttpGet]
public IActionResult GetAll() => Ok("All Orders");
[HttpGet("pending")]
public IActionResult GetPending() => Ok("Pending Orders");
[HttpGet("{id}/details")]
public IActionResult GetDetails(int id) => Ok($"Details for {id}");
}
這樣可產生:
有時候,我們需要從程式中生成對應的 URL(而不是手寫),例如:
[HttpGet("{id}", Name = "GetProductById")]
public IActionResult GetById(int id) => Ok($"Product {id}");
[HttpPost]
public IActionResult Create(Product product)
{
// 透過路由名稱生成 URL
var url = Url.Link("GetProductById", new { id = product.Id });
return Created(url!, product);
}
這樣當新增成功後,系統會自動回傳新建資源的完整 URL。
當專案變大時,可以利用 Areas 來分區管理不同模組的 Controller。
[Area("Admin")]
[Route("api/[area]/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult GetUsers() => Ok("Admin Users");
}
呼叫路徑就會是:
/api/admin/users
這對於大型專案的模組化開發非常實用。
可以根據今天的內容做一個大致的重點整理:
主題 | 重點 |
---|---|
Routing | 決定請求 URL 對應的 Controller 與 Action |
Attribute Routing | 在控制器或方法上以屬性方式定義路由 |
Route Constraints | 限制參數型態(如 int、alpha、datetime) |
URL Generation | 用 Url.Link() 自動生成對應路由 URL |
Areas | 用於大型專案的模組化路由管理 |
Routing 是 API 架構的基礎,決定了如何將請求導向正確的邏輯。理解 Attribute Routing、參數約束 與 Areas,不僅能讓 API 架構更清晰,也讓維護與擴充變得更輕鬆。
明天,我們將進一步探討 Model Binding 與 Validation(資料繫結與驗證),讓 API 不只是能運作,還能正確處理輸入資料!