備註
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
本教學課程將教導建置使用資料庫之控制器型 Web API 的基本概念。 在 ASP.NET Core 中建立 API 的另一種方法是建立最小的 API。 如需在最小 API 與控制器型 API 之間進行選擇的協助,請參閱 API 概觀。 如需建立最小 API 的教學課程,請參閱 教學課程:使用 ASP.NET Core 建立最小 API。
概觀
本教學課程會建立以下 API:
API (應用程式介面) | 描述 | 請求主體 | 回應主體 |
---|---|---|---|
GET /api/todoitems |
取得所有待辦事項 | 無 | 待辦事項的陣列 |
GET /api/todoitems/{id} |
根據識別碼取得項目 | 無 | 待辦事項 |
POST /api/todoitems |
新增項目 | 待辦事項 | 待辦事項 |
PUT /api/todoitems/{id} |
更新現有的項目 | 待辦事項 | 無 |
DELETE /api/todoitems/{id} |
刪除項目 | 無 | 無 |
下圖顯示應用程式的設計。
必要條件
具有 ASP.NET 和 Web 開發工作負載的 Visual Studio 2022。
建立 Web API 專案
- 從 [ 檔案] 功能表中,選取 [ 新增>專案]。
- 在搜尋方塊中輸入 Web API 。
- 選取 [ASP.NET Core Web API 範本],然後選取 [ 下一步]。
- 在 [ 設定新專案] 對話框中,將專案命名為 TodoApi ,然後選取 [ 下一步]。
- 在 [其他資訊] 對話方塊中:
- 確認框架為 .NET 9.0(標準期限支援)。
- 確認已核取 [啟用 OpenAPI 支援 ] 複選框。
- 確認 使用控制器的複選框已勾選(取消勾選以使用簡化 API)。
- 選取 建立。
新增 NuGet 套件
必須新增 NuGet 套件,才能支援本教學課程中使用的資料庫。
- 在 [工具] 功能表上,選取 [NuGet 套件管理員] > [管理解決方案的 NuGet 套件]。
- 選取瀏覽 索引標籤。
- 在搜尋方塊中 輸入 Microsoft.EntityFrameworkCore.InMemory ,然後選取
Microsoft.EntityFrameworkCore.InMemory
。 - 選取右窗格中的 [專案] 核取方塊,然後選取 [安裝]。
執行專案
項目範本會建立一個支援WeatherForecast
的 API。
按 Ctrl+F5 即可執行而不使用偵錯工具。
當專案尚未設定為使用 SSL 時,Visual Studio 會顯示下列對話方塊:
如果您信任 IIS Express SSL 憑證,請選取 [是]。
此時會顯示下列對話方塊:
若您同意信任開發憑證,請選取 [是]。
如需信任 Firefox 瀏覽器的資訊,請參閱 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE憑證錯誤。
Visual Studio 會啟動終端機視窗,並顯示執行中應用程式的 URL。 API 裝載於 https://localhost:<port>
,其中 <port>
是專案建立時設定的隨機選擇埠號碼。
...
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7260
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:7261
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
...
Ctrl+按兩下 輸出中的 HTTPS URL,以在瀏覽器中測試 Web 應用程式。 在 https://localhost:<port>
,沒有端點,因此瀏覽器會回傳 HTTP 404 找不到。
將 /weatherforecast
附加至 URL 以測試 WeatherForecast API。
瀏覽器會顯示類似下列範例的 JSON:
[
{
"date": "2025-07-16",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2025-07-17",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2025-07-18",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2025-07-19",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2025-07-20",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
測試專案
本教學課程使用 端點總管和 .HTTP 檔案 來測試 API。
新增模型類別
「模型」是代表應用程式所管理資料的一組類別。 此應用程式的模型是 TodoItem
類別。
- 在 [方案總管] 中,用滑鼠右鍵點擊專案。 選取 [新增>資料夾]。 將資料夾命名為
Models
。 - 以滑鼠右鍵按一下
Models
資料夾並選取 [新增]>[類別]。 將類別命名為 TodoItem ,然後選取 [ 新增]。 - 以下列項目取代範本程式碼:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
屬性的功能相當於關聯式資料庫中的唯一索引鍵。
模型類別可位於專案中的任何位置,但依照慣例會使用 Models
資料夾。
新增資料庫上下文
「資料庫內容」是為資料模型協調 Entity Framework 功能的主要類別。 此類別是透過衍生自 Microsoft.EntityFrameworkCore.DbContext 類別來建立。
以滑鼠右鍵按一下
Models
資料夾並選取 [新增]>[類別]。 將類別命名為 TodoContext ,然後按兩下 [ 新增]。輸入下列程式碼:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
登錄資料庫內容
在 ASP.NET Core 中,DB 內容之類的服務必須向 相依性插入 (DI) 容器註冊。 此容器會將服務提供給控制器。
使用下列醒目的程式碼更新 Program.cs
:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
上述 程式碼:
- 加入
using
指令。 - 將資料庫內容新增至 DI 容器。
- 指定資料庫內容將會使用記憶體內部資料庫。
生成控制器
以滑鼠右鍵按一下
Controllers
資料夾。選取 新增>New Scaffolded Item.
選取 [使用 Entity Framework 執行動作的 API 控制器],然後選取 [新增]。
在 [新增具有動作且使用 Entity Framework 的 API 控制器] 對話方塊中:
- 在 [模型類別] 中選取 [TodoItem (TodoApi.Models)]。
- 在 資料內容類別中選取 TodoContext (TodoApi.Models)。
- 選取 [新增]。
如果搭建操作失敗,請選取 [新增] 以嘗試再次進行搭建。
此步驟會將 Microsoft.VisualStudio.Web.CodeGeneration.Design
和 Microsoft.EntityFrameworkCore.Tools
NuGet 套件新增至專案。
這些套件是 Scaffolding 所需的。
產生的程式碼:
- 使用
[ApiController]
屬性標記類別。 這個屬性表示控制器會回應 Web API 要求。 如需屬性啟用的特定行為相關信息,請參閱 使用 ASP.NET Core 建立 Web API。 - 使用 DI 將資料庫內容 (
TodoContext
) 插入到控制器中。 資料庫內容用於控制器中每個 CRUD 方法。
ASP.NET Core 範本適用於:
- 路由範本中檢視包含
[action]
的控制器。 - API 控制器在路由範本中不包含
[action]
。
[action]
當令牌不在路由範本中時,動作名稱 (方法名稱) 不會包含在端點中。 也就是說,動作的關聯方法名稱並未用於路由匹配。
更新 PostTodoItem 建立方法
更新 PostTodoItem
中的 return 陳述式,以使用 nameof 運算子:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
上述程式碼是 HTTP POST
方法,以 [HttpPost]
屬性表示。 該方法會從 HTTP 要求本文取得 TodoItem
的值。
如需詳細資訊,請參閱 使用 Http[Verb] 屬性的屬性路由。
CreatedAtAction 方法:
- 如果成功,會傳回 HTTP 201 狀態代碼 。 對於可在伺服器上建立新資源的
HTTP 201
方法,HTTP POST
是標準回應。 - 將 Location 標頭新增至回應。 標頭
Location
會指定新建立 to-do 專案的 URI 。 如需詳細資訊,請參閱 10.2.2 201 已建立。 - 參考
GetTodoItem
動作以建立Location
標頭的 URI。 C#nameof
關鍵字是用來避免在CreatedAtAction
呼叫中以硬式編碼方式寫入動作名稱。
測試 PostTodoItem
選取 檢視>其他視窗>端點總管。
以滑鼠右鍵按一下 [POST] 端點,然後選取 [產生要求]。
隨即會在專案資料夾中建立名為
TodoApi.http
的新檔案,其內容類別似下列範例:@TodoApi_HostAddress = https://localhost:49738 POST {{TodoApi_HostAddress}}/api/todoitems Content-Type: application/json { //TodoItem } ###
- 第一行會建立用於所有端點的變數。
- 下一行會定義 POST 要求。
- POST 要求行之後的行會定義標頭,以及為要求本文提供一個佔位符。
- 三重主題標籤 (
###
) 行是要求分隔符號:其之後則用於不同的要求。
POST 請求需要
TodoItem
。 若要定義 todo,請使用下列 JSON 取代//TodoItem
批註:{ "name": "walk dog", "isComplete": true }
TodoApi.http 檔案現在看起來應該類似下列範例,但有您的連接埠號碼:
@TodoApi_HostAddress = https://localhost:7260 Post {{TodoApi_HostAddress}}/api/todoitems Content-Type: application/json { "name": "walk dog", "isComplete": true } ###
執行應用程式。
選取 要求行上方的 [傳送要求]
POST
連結。POST 要求會傳送至應用程式,而回應會顯示在 [回應] 窗格中。
測試位置標頭中的 URI
透過瀏覽器或端點瀏覽器呼叫端點,以測試應用程式。 下列步驟適用於 端點總管。
在 [端點總管] 中,以滑鼠右鍵按兩下第一個 GET 端點,然後選取 [ 產生要求]。
下列內容會新增至
TodoApi.http
檔案:GET {{TodoApi_HostAddress}}/api/todoitems ###
選取新 要求行上方的 [傳送要求]
GET
連結。GET 要求會傳送至應用程式,而回應會顯示在 [回應] 窗格中。
回應主體類似以下 JSON:
[ { "id": 1, "name": "walk dog", "isComplete": true } ]
在 [端點總管] 中,以滑鼠右鍵按兩下
/api/todoitems/{id}
GET 端點,然後選取 [ 產生要求]。 下列內容會新增至TodoApi.http
檔案:@id=0 GET {{TodoApi_HostAddress}}/api/todoitems/{{id}} ###
將
{@id}
指派給1
(而不是0
)。選取新 GET 要求行上方的 [傳送要求] 連結。
GET 要求會傳送至應用程式,而回應會顯示在 [回應] 窗格中。
回應主體類似以下 JSON:
{ "id": 1, "name": "walk dog", "isComplete": true }
檢查 GET 方法
兩個 GET 端點已經實作:
GET /api/todoitems
GET /api/todoitems/{id}
上一節顯示 /api/todoitems/{id}
路由的範例。
依照 POST 指示新增另一個待辦事項專案,然後使用 Swagger 測試 /api/todoitems
路由。
這個應用程式會使用記憶體內部資料庫。 如果應用程式在停止後再啟動,上述 GET 要求不會傳回任何資料。 如果未傳回任何數據,請將 POST 資料傳送至應用程式。
路由和 URL 路徑
[HttpGet]
屬性代表回應 HTTP GET
要求的方法。 每個方法的 URL 路徑的建構方式如下:
一開始在控制器的
Route
屬性中使用範本字串:[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
以控制器的名稱取代
[controller]
,也就是將控制器類別名稱減去 "Controller" 字尾。 在此範例中,控制器類別名稱為 TodoItems控制器,因此控制器名稱為 「TodoItems」。 ASP.NET 核心 路由 不區分大小寫。如果
[HttpGet]
屬性具有路由範本 (例如[HttpGet("products")]
),請將其附加到路徑。 此範例不使用範本。 如需詳細資訊,請參閱 使用 Http[Verb] 屬性的屬性路由。
在下列 GetTodoItem
方法中,"{id}"
是待辦事項唯一識別碼的預留位置變數。 在叫用 GetTodoItem
時,會將 URL 中的 "{id}"
值提供給方法的 id
參數。
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
傳回值
GetTodoItems
和 GetTodoItem
方法的傳回類型為 ActionResult<T> 類型。 ASP.NET Core 會自動將物件串行化為 JSON ,並將 JSON 寫入回應消息的本文。 此傳回類型的回應碼為 200 OK,假設沒有未處理的例外狀況。 未處理的例外狀況會轉譯成 5xx 錯誤。
ActionResult
傳回型別可代表各種 HTTP 狀態碼。 例如,GetTodoItem
可傳回兩個不同的狀態值:
- 如果沒有符合所請求的 ID 的項目,此方法會傳回 404 狀態NotFound 錯誤碼。
- 否則,該方法會傳回 200 並包含 JSON 回應主體。 傳回
item
結果將得到HTTP 200
回應。
PutTodoItem 方法
檢查 PutTodoItem
方法:
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
類似於 PostTodoItem
,但是會使用 HTTP PUT
。 回應為 204(無內容)。 根據 HTTP 規格,PUT
要求用戶端傳送完整更新的實體,而不只是變更。 若要支援部分更新,請使用 HTTP PATCH。
測試 PutTodoItem 方法
每次啟動應用程式時,此範例使用的記憶體內部資料庫必須被初始化。 資料庫中必須有項目,您才能進行 PUT 呼叫。 在發出 PUT 呼叫之前,呼叫 GET 以確保資料庫中有項目。
使用 PUT
方法來更新識別碼 = 1 的 TodoItem
,並將其名稱設定為 "feed fish"
。 請注意,回應為 HTTP 204 No Content
。
在 [端點總管] 中,以滑鼠右鍵按兩下 PUT 端點,然後選取 [ 產生要求]。
下列內容會新增至
TodoApi.http
檔案:PUT {{TodoApi_HostAddress}}/api/todoitems/{{id}} Content-Type: application/json { //TodoItem } ###
在 PUT 要求行中,將
{{id}}
取代為1
。使用以下內容取代
//TodoItem
佔位符:PUT {{TodoApi_HostAddress}}/api/todoitems/1 Content-Type: application/json { "id": 1, "name": "feed fish", "isComplete": false }
選取新 PUT 要求行上方的 [傳送要求] 連結。
PUT 要求會傳送至應用程式,而回應會顯示在 [回應] 窗格中。 回應本文是空白,而狀態碼為 204。
DeleteTodoItem 方法
檢查 DeleteTodoItem
方法:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
測試 DeleteTodoItem 方法
使用 DELETE
方法來刪除識別碼 = 1 的 TodoItem
。 請注意,回應為 HTTP 204 No Content
。
在 [端點總管] 中,以滑鼠右鍵按兩下 DELETE 端點,然後選取 [ 產生要求]。
將 DELETE 要求新增至
TodoApi.http
。將 DELETE 要求行中的
{{id}}
取代為1
。 DELETE 要求看起來應該類似下列範例:DELETE {{TodoApi_HostAddress}}/api/todoitems/{{id}} ###
選取 DELETE 要求的 傳送要求連結。
DELETE 要求會傳送至應用程式,而回應會顯示在 [回應] 窗格中。 回應本文是空白,而狀態碼為 204。
使用其他工具進行測試
還有其他許多工具可用來測試 Web API,例如:
防止過度張貼
目前範例應用程式會公開整個 TodoItem
物件。 生產應用程式通常會限制輸入的資料,並使用模型的子集傳回。 背後有多個原因,而安全性是主要原因。 模型的子集通常稱為資料傳輸物件 (DTO)、輸入模型或檢視模型。 本教學課程會使用 DTO。
DTO 可以用來:
- 防止過度張貼。
- 隱藏用戶端不應該檢視的屬性。
- 省略一些屬性,以減少承載大小。
- 將包含巢狀物件的物件圖扁平化。 扁平化的物件圖譜可能對客戶端來說更加方便。
若要示範 DTO 方法,請更新 TodoItem
類別以包含祕密欄位:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
必須將祕密欄位從此應用程式中隱藏,但管理應用程式可以選擇顯示它。
驗證您可以提交並取得隱私欄位。
在 Models/TodoItemsDTO.cs 檔案中建立 DTO 模型:
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
更新 TodoItemsController
以使用 TodoItemDTO
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
確認您無法張貼或取得祕密欄位。
使用 JavaScript 呼叫 Web API
請參閱 教學課程:使用 JavaScript 呼叫 ASP.NET Core Web API。
Web API 影片系列
請參閱 影片:初學者系列 — 網頁 API。
企業 Web 應用程式模式
如需建立可靠、安全、高效能、可測試且可調整 ASP.NET 核心應用程式的指引,請參閱 企業 Web 應用程式模式。 提供可實作模式的完整生產品質範例 Web 應用程式。
將驗證支援新增至 Web API
ASP.NET Core Identity 會將使用者介面 (UI) 登入功能新增至 ASP.NET Core Web 應用程式。 若要保護 Web API 和 SPA,請使用下列其中一項:
- Microsoft Entra 身份識別
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity 伺服器
Duende Identity 伺服器是適用於 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 架構。 Duende Identity 伺服器會啟用下列安全性功能:
- 驗證即服務 (AaaS)
- 多個應用程式類型的單一登入/登出 (SSO)
- API 的存取控制
- 同盟閘道
重要
Duende Software 可能會要求您支付使用 Duende Identity 伺服器的授權費用。 如需詳細資訊,請參閱 從 .NET 5 中的 ASP.NET Core 移轉至 .NET 6。
如需詳細資訊,請參閱 Duende 伺服器檔(Duende Identity Software 網站)。
發佈至 Azure
如需部署至 Azure 的資訊,請參閱 快速入門:部署 ASP.NET Web 應用程式。
其他資源
檢視或下載本教學課程的範例程序代碼。 請參閱 如何下載。
如需詳細資訊,請參閱以下資源:
本教學課程將教導建置使用資料庫之控制器型 Web API 的基本概念。 在 ASP.NET Core 中建立 API 的另一種方法是建立最小的 API。 如需在最小 API 與控制器型 API 之間進行選擇的協助,請參閱 API 概觀。 如需建立最小 API 的教學課程,請參閱 教學課程:使用 ASP.NET Core 建立最小 API。
概觀
本教學課程會建立以下 API:
API (應用程式介面) | 描述 | 請求主體 | 回應主體 |
---|---|---|---|
GET /api/todoitems |
取得所有待辦事項 | 無 | 待辦事項的陣列 |
GET /api/todoitems/{id} |
根據識別碼取得項目 | 無 | 待辦事項 |
POST /api/todoitems |
新增項目 | 待辦事項 | 待辦事項 |
PUT /api/todoitems/{id} |
更新現有的項目 | 待辦事項 | 無 |
DELETE /api/todoitems/{id} |
刪除項目 | 無 | 無 |
下圖顯示應用程式的設計。
必要條件
具有 ASP.NET 和 Web 開發工作負載的 Visual Studio 2022。
建立 Web 專案
- 從 [ 檔案] 功能表中,選取 [ 新增>專案]。
- 在搜尋方塊中輸入 Web API 。
- 選取 [ASP.NET Core Web API 範本],然後選取 [ 下一步]。
- 在 [ 設定新專案] 對話框中,將專案命名為 TodoApi ,然後選取 [ 下一步]。
- 在 [其他資訊] 對話方塊中:
- 確認架構為 .NET 8.0(長期支援)。
- 確認已勾選 [使用控制器] 複選框(若取消勾選,將使用最少 API)。
- 確認已核取 [啟用 OpenAPI 支援 ] 複選框。
- 選取 建立。
新增 NuGet 套件
必須新增 NuGet 套件,才能支援本教學課程中使用的資料庫。
- 在 [工具] 功能表上,選取 [NuGet 套件管理員] > [管理解決方案的 NuGet 套件]。
- 選取瀏覽 索引標籤。
- 在搜尋方塊中 輸入 Microsoft.EntityFrameworkCore.InMemory ,然後選取
Microsoft.EntityFrameworkCore.InMemory
。 - 選取右窗格中的 [專案] 核取方塊,然後選取 [安裝]。
測試專案
專案範本會建立一個支援 Swagger 的 WeatherForecast
API。
按 Ctrl+F5 即可執行而不使用偵錯工具。
當專案尚未設定為使用 SSL 時,Visual Studio 會顯示下列對話方塊:
如果您信任 IIS Express SSL 憑證,請選取 [是]。
此時會顯示下列對話方塊:
若您同意信任開發憑證,請選取 [是]。
如需信任 Firefox 瀏覽器的資訊,請參閱 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE憑證錯誤。
Visual Studio 會啟動預設瀏覽器並瀏覽至 https://localhost:<port>/swagger/index.html
,其中 <port>
是專案建立時設定的隨機選擇連接埠號碼。
Swagger 頁面 /swagger/index.html
隨即顯示。 選取 GET>試試看>執行。 頁面會顯示:
- 用來測試 WeatherForecast API 的 Curl 命令。
- 用來測試 WeatherForecast API 的 URL。
- 回應碼、本文和標頭。
- 具有媒體類型與範例值和架構的下拉式清單方塊。
如果 Swagger 頁面未出現,請參閱 此 GitHub 問題。
Swagger 可用來為 Web API 產生有用的文件和說明頁面。 本教學課程會使用 Swagger 來測試應用程式。 如需 Swagger 的詳細資訊,請參閱 使用 Swagger / OpenAPI ASP.NET Core Web API 檔。
複製並貼上瀏覽器中 的要求 URL : https://localhost:<port>/weatherforecast
系統會傳回類似下列範例的 JSON:
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
新增模型類別
「模型」是代表應用程式所管理資料的一組類別。 此應用程式的模型是 TodoItem
類別。
- 在 [方案總管] 中,用滑鼠右鍵點擊專案。 選取 [新增>資料夾]。 將資料夾命名為
Models
。 - 以滑鼠右鍵按一下
Models
資料夾並選取 [新增]>[類別]。 將類別命名為 TodoItem ,然後選取 [ 新增]。 - 以下列項目取代範本程式碼:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
屬性的功能相當於關聯式資料庫中的唯一索引鍵。
模型類別可位於專案中的任何位置,但依照慣例會使用 Models
資料夾。
新增資料庫上下文
「資料庫內容」是為資料模型協調 Entity Framework 功能的主要類別。 此類別是透過衍生自 Microsoft.EntityFrameworkCore.DbContext 類別來建立。
- 以滑鼠右鍵按一下
Models
資料夾並選取 [新增]>[類別]。 將類別命名為 TodoContext ,然後按兩下 [ 新增]。
輸入下列程式碼:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
登錄資料庫內容
在 ASP.NET Core 中,DB 內容之類的服務必須向 相依性插入 (DI) 容器註冊。 此容器會將服務提供給控制器。
使用下列醒目的程式碼更新 Program.cs
:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
上述 程式碼:
- 加入
using
指令。 - 將資料庫內容新增至 DI 容器。
- 指定資料庫內容將會使用記憶體內部資料庫。
生成控制器
以滑鼠右鍵按一下
Controllers
資料夾。選取 新增>New Scaffolded Item.
選取 [使用 Entity Framework 執行動作的 API 控制器],然後選取 [新增]。
在 [新增具有動作且使用 Entity Framework 的 API 控制器] 對話方塊中:
- 在 [模型類別] 中選取 [TodoItem (TodoApi.Models)]。
- 在 資料內容類別中選取 TodoContext (TodoApi.Models)。
- 選取 [新增]。
如果搭建操作失敗,請選取 [新增] 以嘗試再次進行搭建。
產生的程式碼:
- 使用
[ApiController]
屬性標記類別。 這個屬性表示控制器會回應 Web API 要求。 如需屬性啟用的特定行為相關信息,請參閱 使用 ASP.NET Core 建立 Web API。 - 使用 DI 將資料庫內容 (
TodoContext
) 插入到控制器中。 資料庫內容用於控制器中每個 CRUD 方法。
ASP.NET Core 範本適用於:
- 路由範本中檢視包含
[action]
的控制器。 - API 控制器在路由範本中不包含
[action]
。
[action]
當令牌不在路由範本中時,動作名稱 (方法名稱) 不會包含在端點中。 也就是說,動作的關聯方法名稱並未用於路由匹配。
更新 PostTodoItem 建立方法
更新 PostTodoItem
中的 return 陳述式,以使用 nameof 運算子:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
上述程式碼是 HTTP POST
方法,以 [HttpPost]
屬性表示。 該方法會從 HTTP 要求本文取得 TodoItem
的值。
如需詳細資訊,請參閱 使用 Http[Verb] 屬性的屬性路由。
CreatedAtAction 方法:
- 如果成功,會傳回 HTTP 201 狀態代碼 。 對於可在伺服器上建立新資源的
HTTP 201
方法,HTTP POST
是標準回應。 - 將 Location 標頭新增至回應。 標頭
Location
會指定新建立 to-do 專案的 URI 。 如需詳細資訊,請參閱 10.2.2 201 已建立。 - 參考
GetTodoItem
動作以建立Location
標頭的 URI。 C#nameof
關鍵字是用來避免在CreatedAtAction
呼叫中以硬式編碼方式寫入動作名稱。
測試 PostTodoItem
按 Ctrl+F5 執行應用程式。
在 Swagger 瀏覽器視窗中,選取 [POST /api/TodoItems],然後選取 [試用]。
在 請求主體 輸入視窗中,更新 JSON。 例如,
{ "name": "walk dog", "isComplete": true }
選取 [執行]
測試位置標頭中的 URI
在上述 POST 中,Swagger UI 會顯示 回應標頭底下的location 標頭。 例如: location: https://localhost:7260/api/TodoItems/1
。 位置標頭會顯示所建立資源的 URI。
若要測試位置標頭:
在 Swagger 瀏覽器視窗中,選取 [GET /api/TodoItems/{id}],然後選取 [試用]。
在
1
輸入方塊中輸入id
,然後選取 [執行]。
檢查 GET 方法
兩個 GET 端點已經實作:
GET /api/todoitems
GET /api/todoitems/{id}
上一節顯示 /api/todoitems/{id}
路由的範例。
依照 POST 指示新增另一個待辦事項專案,然後使用 Swagger 測試 /api/todoitems
路由。
這個應用程式會使用記憶體內部資料庫。 如果應用程式在停止後再啟動,上述 GET 要求不會傳回任何資料。 如果未傳回任何數據,請將 POST 資料傳送至應用程式。
路由和 URL 路徑
[HttpGet]
屬性代表回應 HTTP GET
要求的方法。 每個方法的 URL 路徑的建構方式如下:
一開始在控制器的
Route
屬性中使用範本字串:[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
以控制器的名稱取代
[controller]
,也就是將控制器類別名稱減去 "Controller" 字尾。 在此範例中,控制器類別名稱為 TodoItems控制器,因此控制器名稱為 「TodoItems」。 ASP.NET 核心 路由 不區分大小寫。如果
[HttpGet]
屬性具有路由範本 (例如[HttpGet("products")]
),請將其附加到路徑。 此範例不使用範本。 如需詳細資訊,請參閱 使用 Http[Verb] 屬性的屬性路由。
在下列 GetTodoItem
方法中,"{id}"
是待辦事項唯一識別碼的預留位置變數。 在叫用 GetTodoItem
時,會將 URL 中的 "{id}"
值提供給方法的 id
參數。
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
傳回值
GetTodoItems
和 GetTodoItem
方法的傳回類型為 ActionResult<T> 類型。 ASP.NET Core 會自動將物件串行化為 JSON ,並將 JSON 寫入回應消息的本文。 此傳回類型的回應碼為 200 OK,假設沒有未處理的例外狀況。 未處理的例外狀況會轉譯成 5xx 錯誤。
ActionResult
傳回型別可代表各種 HTTP 狀態碼。 例如,GetTodoItem
可傳回兩個不同的狀態值:
- 如果沒有符合所請求的 ID 的項目,此方法會傳回 404 狀態NotFound 錯誤碼。
- 否則,該方法會傳回 200 並包含 JSON 回應主體。 傳回
item
結果將得到HTTP 200
回應。
PutTodoItem 方法
檢查 PutTodoItem
方法:
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
類似於 PostTodoItem
,但是會使用 HTTP PUT
。 回應為 204(無內容)。 根據 HTTP 規格,PUT
要求用戶端傳送完整更新的實體,而不只是變更。 若要支援部分更新,請使用 HTTP PATCH。
測試 PutTodoItem 方法
每次啟動應用程式時,此範例使用的記憶體內部資料庫必須被初始化。 資料庫中必須有項目,您才能進行 PUT 呼叫。 在發出 PUT 呼叫之前,呼叫 GET 以確保資料庫中有項目。
使用 Swagger UI,使用 PUT 按鈕來更新識別碼為 1 的 TodoItem
,並將其名稱設定為 "feed fish"
。 請注意,回應為 HTTP 204 No Content
。
DeleteTodoItem 方法
檢查 DeleteTodoItem
方法:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
測試 DeleteTodoItem 方法
使用 Swagger UI 刪除識別碼為 1 的 TodoItem
。 請注意,回應為 HTTP 204 No Content
。
使用其他工具進行測試
還有其他許多工具可用來測試 Web API,例如:
- Visual Studio 端點總管和 .http 檔案
- http-repl
-
curl。 Swagger 會使用
curl
並顯示其提交的curl
命令。 - 琴師
如需詳細資訊,請參閱
防止過度張貼
目前範例應用程式會公開整個 TodoItem
物件。 生產應用程式通常會限制輸入的資料,並使用模型的子集傳回。 背後有多個原因,而安全性是主要原因。 模型的子集通常稱為資料傳輸物件 (DTO)、輸入模型或檢視模型。 本教學課程會使用 DTO。
DTO 可以用來:
- 防止過度張貼。
- 隱藏用戶端不應該檢視的屬性。
- 省略一些屬性,以減少承載大小。
- 將包含巢狀物件的物件圖扁平化。 扁平化的物件圖譜可能對客戶端來說更加方便。
若要示範 DTO 方法,請更新 TodoItem
類別以包含祕密欄位:
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
必須將祕密欄位從此應用程式中隱藏,但管理應用程式可以選擇顯示它。
驗證您可以提交並取得隱私欄位。
建立 DTO 模型:
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
更新 TodoItemsController
以使用 TodoItemDTO
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
確認您無法張貼或取得祕密欄位。
使用 JavaScript 呼叫 Web API
請參閱 教學課程:使用 JavaScript 呼叫 ASP.NET Core Web API。
Web API 影片系列
請參閱 影片:初學者系列 — 網頁 API。
企業 Web 應用程式模式
如需建立可靠、安全、高效能、可測試且可調整 ASP.NET 核心應用程式的指引,請參閱 企業 Web 應用程式模式。 提供可實作模式的完整生產品質範例 Web 應用程式。
將驗證支援新增至 Web API
ASP.NET Core Identity 會將使用者介面 (UI) 登入功能新增至 ASP.NET Core Web 應用程式。 若要保護 Web API 和 SPA,請使用下列其中一項:
- Microsoft Entra 身份識別
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity 伺服器
Duende Identity 伺服器是適用於 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 架構。 Duende Identity 伺服器會啟用下列安全性功能:
- 驗證即服務 (AaaS)
- 多個應用程式類型的單一登入/登出 (SSO)
- API 的存取控制
- 同盟閘道
重要
Duende Software 可能會要求您支付使用 Duende Identity 伺服器的授權費用。 如需詳細資訊,請參閱 從 .NET 5 中的 ASP.NET Core 移轉至 .NET 6。
如需詳細資訊,請參閱 Duende 伺服器檔(Duende Identity Software 網站)。
發佈至 Azure
如需部署至 Azure 的資訊,請參閱 快速入門:部署 ASP.NET Web 應用程式。
其他資源
檢視或下載本教學課程的範例程序代碼。 請參閱 如何下載。
如需詳細資訊,請參閱以下資源:
本教學課程將教導建置使用資料庫之控制器型 Web API 的基本概念。 在 ASP.NET Core 中建立 API 的另一種方法是建立最小的 API。 如需在最小 API 與控制器型 API 之間進行選擇的協助,請參閱 API 概觀。 如需建立最小 API 的教學課程,請參閱 教學課程:使用 ASP.NET Core 建立最小 API。
概觀
本教學課程會建立以下 API:
API (應用程式介面) | 描述 | 請求主體 | 回應主體 |
---|---|---|---|
GET /api/todoitems |
取得所有待辦事項 | 無 | 待辦事項的陣列 |
GET /api/todoitems/{id} |
根據識別碼取得項目 | 無 | 待辦事項 |
POST /api/todoitems |
新增項目 | 待辦事項 | 待辦事項 |
PUT /api/todoitems/{id} |
更新現有的項目 | 待辦事項 | 無 |
DELETE /api/todoitems/{id} |
刪除項目 | 無 | 無 |
下圖顯示應用程式的設計。
必要條件
具有 ASP.NET 和 Web 開發工作負載的 Visual Studio 2022。
建立 Web 專案
- 從 [ 檔案] 功能表中,選取 [ 新增>專案]。
- 在搜尋方塊中輸入 Web API 。
- 選取 [ASP.NET Core Web API 範本],然後選取 [ 下一步]。
- 在 [ 設定新專案] 對話框中,將專案命名為 TodoApi ,然後選取 [ 下一步]。
- 在 [其他資訊] 對話方塊中:
- 確認架構為 .NET 8.0(長期支援)。
- 確認已勾選 [使用控制器] 複選框(若取消勾選,將使用最少 API)。
- 確認已核取 [啟用 OpenAPI 支援 ] 複選框。
- 選取 建立。
新增 NuGet 套件
必須新增 NuGet 套件,才能支援本教學課程中使用的資料庫。
- 在 [工具] 功能表上,選取 [NuGet 套件管理員] > [管理解決方案的 NuGet 套件]。
- 選取瀏覽 索引標籤。
- 在搜尋方塊中 輸入 Microsoft.EntityFrameworkCore.InMemory ,然後選取
Microsoft.EntityFrameworkCore.InMemory
。 - 選取右窗格中的 [專案] 核取方塊,然後選取 [安裝]。
測試專案
專案範本會建立一個支援 Swagger 的 WeatherForecast
API。
按 Ctrl+F5 即可執行而不使用偵錯工具。
當專案尚未設定為使用 SSL 時,Visual Studio 會顯示下列對話方塊:
如果您信任 IIS Express SSL 憑證,請選取 [是]。
此時會顯示下列對話方塊:
若您同意信任開發憑證,請選取 [是]。
如需信任 Firefox 瀏覽器的資訊,請參閱 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE憑證錯誤。
Visual Studio 會啟動預設瀏覽器並瀏覽至 https://localhost:<port>/swagger/index.html
,其中 <port>
是專案建立時設定的隨機選擇連接埠號碼。
Swagger 頁面 /swagger/index.html
隨即顯示。 選取 GET>試試看>執行。 頁面會顯示:
- 用來測試 WeatherForecast API 的 Curl 命令。
- 用來測試 WeatherForecast API 的 URL。
- 回應碼、本文和標頭。
- 具有媒體類型與範例值和架構的下拉式清單方塊。
如果 Swagger 頁面未出現,請參閱 此 GitHub 問題。
Swagger 可用來為 Web API 產生有用的文件和說明頁面。 本教學課程會使用 Swagger 來測試應用程式。 如需 Swagger 的詳細資訊,請參閱 使用 Swagger / OpenAPI ASP.NET Core Web API 檔。
複製並貼上瀏覽器中 的要求 URL : https://localhost:<port>/weatherforecast
系統會傳回類似下列範例的 JSON:
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
新增模型類別
「模型」是代表應用程式所管理資料的一組類別。 此應用程式的模型是 TodoItem
類別。
- 在 [方案總管] 中,用滑鼠右鍵點擊專案。 選取 [新增>資料夾]。 將資料夾命名為
Models
。 - 以滑鼠右鍵按一下
Models
資料夾並選取 [新增]>[類別]。 將類別命名為 TodoItem ,然後選取 [ 新增]。 - 以下列項目取代範本程式碼:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Id
屬性的功能相當於關聯式資料庫中的唯一索引鍵。
模型類別可位於專案中的任何位置,但依照慣例會使用 Models
資料夾。
新增資料庫上下文
「資料庫內容」是為資料模型協調 Entity Framework 功能的主要類別。 此類別是透過衍生自 Microsoft.EntityFrameworkCore.DbContext 類別來建立。
- 以滑鼠右鍵按一下
Models
資料夾並選取 [新增]>[類別]。 將類別命名為 TodoContext ,然後按兩下 [ 新增]。
輸入下列程式碼:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
登錄資料庫內容
在 ASP.NET Core 中,DB 內容之類的服務必須向 相依性插入 (DI) 容器註冊。 此容器會將服務提供給控制器。
使用下列醒目的程式碼更新 Program.cs
:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
上述 程式碼:
- 加入
using
指令。 - 將資料庫內容新增至 DI 容器。
- 指定資料庫內容將會使用記憶體內部資料庫。
生成控制器
以滑鼠右鍵按一下
Controllers
資料夾。選取 新增>New Scaffolded Item.
選取 [使用 Entity Framework 執行動作的 API 控制器],然後選取 [新增]。
在 [新增具有動作且使用 Entity Framework 的 API 控制器] 對話方塊中:
- 在 [模型類別] 中選取 [TodoItem (TodoApi.Models)]。
- 在 資料內容類別中選取 TodoContext (TodoApi.Models)。
- 選取 [新增]。
如果搭建操作失敗,請選取 [新增] 以嘗試再次進行搭建。
產生的程式碼:
- 使用
[ApiController]
屬性標記類別。 這個屬性表示控制器會回應 Web API 要求。 如需屬性啟用的特定行為相關信息,請參閱 使用 ASP.NET Core 建立 Web API。 - 使用 DI 將資料庫內容 (
TodoContext
) 插入到控制器中。 資料庫內容用於控制器中每個 CRUD 方法。
ASP.NET Core 範本適用於:
- 路由範本中檢視包含
[action]
的控制器。 - API 控制器在路由範本中不包含
[action]
。
[action]
當令牌不在路由範本中時,動作名稱 (方法名稱) 不會包含在端點中。 也就是說,動作的關聯方法名稱並未用於路由匹配。
更新 PostTodoItem 建立方法
更新 PostTodoItem
中的 return 陳述式,以使用 nameof 運算子:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
上述程式碼是 HTTP POST
方法,以 [HttpPost]
屬性表示。 該方法會從 HTTP 要求本文取得 TodoItem
的值。
如需詳細資訊,請參閱 使用 Http[Verb] 屬性的屬性路由。
CreatedAtAction 方法:
- 如果成功,會傳回 HTTP 201 狀態代碼 。 對於可在伺服器上建立新資源的
HTTP 201
方法,HTTP POST
是標準回應。 - 將 Location 標頭新增至回應。 標頭
Location
會指定新建立 to-do 專案的 URI 。 如需詳細資訊,請參閱 10.2.2 201 已建立。 - 參考
GetTodoItem
動作以建立Location
標頭的 URI。 C#nameof
關鍵字是用來避免在CreatedAtAction
呼叫中以硬式編碼方式寫入動作名稱。
測試 PostTodoItem
按 Ctrl+F5 執行應用程式。
在 Swagger 瀏覽器視窗中,選取 [POST /api/TodoItems],然後選取 [試用]。
在 請求主體 輸入視窗中,更新 JSON。 例如,
{ "name": "walk dog", "isComplete": true }
選取 [執行]
測試位置標頭中的 URI
在上述 POST 中,Swagger UI 會顯示 回應標頭底下的location 標頭。 例如: location: https://localhost:7260/api/TodoItems/1
。 位置標頭會顯示所建立資源的 URI。
若要測試位置標頭:
在 Swagger 瀏覽器視窗中,選取 [GET /api/TodoItems/{id}],然後選取 [試用]。
在
1
輸入方塊中輸入id
,然後選取 [執行]。
檢查 GET 方法
兩個 GET 端點已經實作:
GET /api/todoitems
GET /api/todoitems/{id}
上一節顯示 /api/todoitems/{id}
路由的範例。
依照 POST 指示新增另一個待辦事項專案,然後使用 Swagger 測試 /api/todoitems
路由。
這個應用程式會使用記憶體內部資料庫。 如果應用程式在停止後再啟動,上述 GET 要求不會傳回任何資料。 如果未傳回任何數據,請將 POST 資料傳送至應用程式。
路由和 URL 路徑
[HttpGet]
屬性代表回應 HTTP GET
要求的方法。 每個方法的 URL 路徑的建構方式如下:
一開始在控制器的
Route
屬性中使用範本字串:[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
以控制器的名稱取代
[controller]
,也就是將控制器類別名稱減去 "Controller" 字尾。 在此範例中,控制器類別名稱為 TodoItems控制器,因此控制器名稱為 「TodoItems」。 ASP.NET 核心 路由 不區分大小寫。如果
[HttpGet]
屬性具有路由範本 (例如[HttpGet("products")]
),請將其附加到路徑。 此範例不使用範本。 如需詳細資訊,請參閱 使用 Http[Verb] 屬性的屬性路由。
在下列 GetTodoItem
方法中,"{id}"
是待辦事項唯一識別碼的預留位置變數。 在叫用 GetTodoItem
時,會將 URL 中的 "{id}"
值提供給方法的 id
參數。
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
傳回值
GetTodoItems
和 GetTodoItem
方法的傳回類型為 ActionResult<T> 類型。 ASP.NET Core 會自動將物件串行化為 JSON ,並將 JSON 寫入回應消息的本文。 此傳回類型的回應碼為 200 OK,假設沒有未處理的例外狀況。 未處理的例外狀況會轉譯成 5xx 錯誤。
ActionResult
傳回型別可代表各種 HTTP 狀態碼。 例如,GetTodoItem
可傳回兩個不同的狀態值:
- 如果沒有符合所請求的 ID 的項目,此方法會傳回 404 狀態NotFound 錯誤碼。
- 否則,該方法會傳回 200 並包含 JSON 回應主體。 傳回
item
結果將得到HTTP 200
回應。
PutTodoItem 方法
檢查 PutTodoItem
方法:
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
類似於 PostTodoItem
,但是會使用 HTTP PUT
。 回應為 204(無內容)。 根據 HTTP 規格,PUT
要求用戶端傳送完整更新的實體,而不只是變更。 若要支援部分更新,請使用 HTTP PATCH。
測試 PutTodoItem 方法
每次啟動應用程式時,此範例使用的記憶體內部資料庫必須被初始化。 資料庫中必須有項目,您才能進行 PUT 呼叫。 在發出 PUT 呼叫之前,呼叫 GET 以確保資料庫中有項目。
使用 Swagger UI,使用 PUT 按鈕來更新識別碼為 1 的 TodoItem
,並將其名稱設定為 "feed fish"
。 請注意,回應為 HTTP 204 No Content
。
DeleteTodoItem 方法
檢查 DeleteTodoItem
方法:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
測試 DeleteTodoItem 方法
使用 Swagger UI 刪除識別碼為 1 的 TodoItem
。 請注意,回應為 HTTP 204 No Content
。
使用其他工具進行測試
還有其他許多工具可用來測試 Web API,例如:
- Visual Studio 端點總管和 .http 檔案
- http-repl
-
curl。 Swagger 會使用
curl
並顯示其提交的curl
命令。 - 琴師
如需詳細資訊,請參閱
防止過度張貼
目前範例應用程式會公開整個 TodoItem
物件。 生產應用程式通常會限制輸入的資料,並使用模型的子集傳回。 背後有多個原因,而安全性是主要原因。 模型的子集通常稱為資料傳輸物件 (DTO)、輸入模型或檢視模型。 本教學課程會使用 DTO。
DTO 可以用來:
- 防止過度張貼。
- 隱藏用戶端不應該檢視的屬性。
- 省略一些屬性,以減少承載大小。
- 將包含巢狀物件的物件圖扁平化。 扁平化的物件圖譜可能對客戶端來說更加方便。
若要示範 DTO 方法,請更新 TodoItem
類別以包含祕密欄位:
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
必須將祕密欄位從此應用程式中隱藏,但管理應用程式可以選擇顯示它。
驗證您可以提交並取得隱私欄位。
建立 DTO 模型:
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
更新 TodoItemsController
以使用 TodoItemDTO
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
確認您無法張貼或取得祕密欄位。
使用 JavaScript 呼叫 Web API
請參閱 教學課程:使用 JavaScript 呼叫 ASP.NET Core Web API。
Web API 影片系列
請參閱 影片:初學者系列 — 網頁 API。
企業 Web 應用程式模式
如需建立可靠、安全、高效能、可測試且可調整 ASP.NET 核心應用程式的指引,請參閱 企業 Web 應用程式模式。 提供可實作模式的完整生產品質範例 Web 應用程式。
將驗證支援新增至 Web API
ASP.NET Core Identity 會將使用者介面 (UI) 登入功能新增至 ASP.NET Core Web 應用程式。 若要保護 Web API 和 SPA,請使用下列其中一項:
- Microsoft Entra 身份識別
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity 伺服器
Duende Identity 伺服器是適用於 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 架構。 Duende Identity 伺服器會啟用下列安全性功能:
- 驗證即服務 (AaaS)
- 多個應用程式類型的單一登入/登出 (SSO)
- API 的存取控制
- 同盟閘道
重要
Duende Software 可能會要求您支付使用 Duende Identity 伺服器的授權費用。 如需詳細資訊,請參閱 從 .NET 5 中的 ASP.NET Core 移轉至 .NET 6。
如需詳細資訊,請參閱 Duende 伺服器檔(Duende Identity Software 網站)。
發佈至 Azure
如需部署至 Azure 的資訊,請參閱 快速入門:部署 ASP.NET Web 應用程式。
其他資源
檢視或下載本教學課程的範例程序代碼。 請參閱 如何下載。
如需詳細資訊,請參閱以下資源: