共用方式為


教學課程:使用 ASP.NET Core 建立控制器型 Web API

備註

這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本

警告

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不提供任何明示或暗示的保證。

如需目前的版本,請參閱 本文的 .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}     刪除項目

下圖顯示應用程式的設計。

用戶端會以左邊的方塊表示。它會提交要求,並接收來自應用程式的回應,右側繪製的方塊。在應用程式方塊中,三個方塊代表控制器、模型和數據存取層。要求會進入應用程式的控制器,而且控制器和數據存取層之間會發生讀取/寫入作業。模型會在回應中串行化並傳回至用戶端。

必要條件

建立 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
  • 選取右窗格中的 [專案] 核取方塊,然後選取 [安裝]

備註

如需將套件新增至 .NET 應用程式的指引,請參閱套件取用工作流程 (NuGet 文件)下的安裝與管理套件文章。 在 NuGet.org 確認正確的套件版本。

執行專案

項目範本會建立一個支援WeatherForecast API。

按 Ctrl+F5 即可執行而不使用偵錯工具。

當專案尚未設定為使用 SSL 時,Visual Studio 會顯示下列對話方塊:

此項目已設定為使用SSL。若要避免瀏覽器中的 SSL 警告,您可以選擇信任 IIS Express 產生的自我簽署憑證。您要信任 IIS Express SSL 憑證嗎?

如果您信任 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.DesignMicrosoft.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] 端點,然後選取 [產生要求]

    Endpoints Explorer 快捷功能表中突顯 [產生要求] 功能選項。

    隨即會在專案資料夾中建立名為 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 連結。

    醒目提示執行連結的 .HTTP 檔案視窗。

    POST 要求會傳送至應用程式,而回應會顯示在 [回應] 窗格中。

    具有 POST 要求回應的 .http 檔案視窗。

測試位置標頭中的 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;
}

傳回值

GetTodoItemsGetTodoItem 方法的傳回類型為 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,請使用下列其中一項:

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}     刪除項目

下圖顯示應用程式的設計。

用戶端會以左邊的方塊表示。它會提交要求,並接收來自應用程式的回應,右側繪製的方塊。在應用程式方塊中,三個方塊代表控制器、模型和數據存取層。要求會進入應用程式的控制器,而且控制器和數據存取層之間會發生讀取/寫入作業。模型會在回應中串行化並傳回至用戶端。

必要條件

建立 Web 專案

  • 從 [ 檔案] 功能表中,選取 [ 新增>專案]。
  • 在搜尋方塊中輸入 Web API
  • 選取 [ASP.NET Core Web API 範本],然後選取 [ 下一步]。
  • 在 [ 設定新專案] 對話框中,將專案命名為 TodoApi ,然後選取 [ 下一步]。
  • 在 [其他資訊] 對話方塊中:
    • 確認架構.NET 8.0(長期支援)。
    • 確認已勾選 [使用控制器] 複選框(若取消勾選,將使用最少 API)
    • 確認已核取 [啟用 OpenAPI 支援 ] 複選框。
    • 選取 建立

新增 NuGet 套件

必須新增 NuGet 套件,才能支援本教學課程中使用的資料庫。

  • 在 [工具] 功能表上,選取 [NuGet 套件管理員] > [管理解決方案的 NuGet 套件]
  • 選取瀏覽 索引標籤。
  • 在搜尋方塊中 輸入 Microsoft.EntityFrameworkCore.InMemory ,然後選取 Microsoft.EntityFrameworkCore.InMemory
  • 選取右窗格中的 [專案] 核取方塊,然後選取 [安裝]

備註

如需將套件新增至 .NET 應用程式的指引,請參閱套件取用工作流程 (NuGet 文件)下的安裝與管理套件文章。 在 NuGet.org 確認正確的套件版本。

測試專案

專案範本會建立一個支援 Swagger 的 WeatherForecast API。

按 Ctrl+F5 即可執行而不使用偵錯工具。

當專案尚未設定為使用 SSL 時,Visual Studio 會顯示下列對話方塊:

此項目已設定為使用SSL。若要避免瀏覽器中的 SSL 警告,您可以選擇信任 IIS Express 產生的自我簽署憑證。您要信任 IIS Express SSL 憑證嗎?

如果您信任 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 檔

複製並貼上瀏覽器中 的要求 URLhttps://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
    }
    
  • 選取 [執行]

    Swagger POST

測試位置標頭中的 URI

在上述 POST 中,Swagger UI 會顯示 回應標頭底下的location 標頭。 例如: location: https://localhost:7260/api/TodoItems/1 。 位置標頭會顯示所建立資源的 URI。

若要測試位置標頭:

  • 在 Swagger 瀏覽器視窗中,選取 [GET /api/TodoItems/{id}],然後選取 [試用]

  • 1 輸入方塊中輸入 id,然後選取 [執行]

    Swagger GET

檢查 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;
}

傳回值

GetTodoItemsGetTodoItem 方法的傳回類型為 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,例如:

如需詳細資訊,請參閱

防止過度張貼

目前範例應用程式會公開整個 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,請使用下列其中一項:

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}     刪除項目

下圖顯示應用程式的設計。

用戶端會以左邊的方塊表示。它會提交要求,並接收來自應用程式的回應,右側繪製的方塊。在應用程式方塊中,三個方塊代表控制器、模型和數據存取層。要求會進入應用程式的控制器,而且控制器和數據存取層之間會發生讀取/寫入作業。模型會在回應中串行化並傳回至用戶端。

必要條件

建立 Web 專案

  • 從 [ 檔案] 功能表中,選取 [ 新增>專案]。
  • 在搜尋方塊中輸入 Web API
  • 選取 [ASP.NET Core Web API 範本],然後選取 [ 下一步]。
  • 在 [ 設定新專案] 對話框中,將專案命名為 TodoApi ,然後選取 [ 下一步]。
  • 在 [其他資訊] 對話方塊中:
    • 確認架構.NET 8.0(長期支援)。
    • 確認已勾選 [使用控制器] 複選框(若取消勾選,將使用最少 API)
    • 確認已核取 [啟用 OpenAPI 支援 ] 複選框。
    • 選取 建立

新增 NuGet 套件

必須新增 NuGet 套件,才能支援本教學課程中使用的資料庫。

  • 在 [工具] 功能表上,選取 [NuGet 套件管理員] > [管理解決方案的 NuGet 套件]
  • 選取瀏覽 索引標籤。
  • 在搜尋方塊中 輸入 Microsoft.EntityFrameworkCore.InMemory ,然後選取 Microsoft.EntityFrameworkCore.InMemory
  • 選取右窗格中的 [專案] 核取方塊,然後選取 [安裝]

備註

如需將套件新增至 .NET 應用程式的指引,請參閱套件取用工作流程 (NuGet 文件)下的安裝與管理套件文章。 在 NuGet.org 確認正確的套件版本。

測試專案

專案範本會建立一個支援 Swagger 的 WeatherForecast API。

按 Ctrl+F5 即可執行而不使用偵錯工具。

當專案尚未設定為使用 SSL 時,Visual Studio 會顯示下列對話方塊:

此項目已設定為使用SSL。若要避免瀏覽器中的 SSL 警告,您可以選擇信任 IIS Express 產生的自我簽署憑證。您要信任 IIS Express SSL 憑證嗎?

如果您信任 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 檔

複製並貼上瀏覽器中 的要求 URLhttps://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
    }
    
  • 選取 [執行]

    Swagger POST

測試位置標頭中的 URI

在上述 POST 中,Swagger UI 會顯示 回應標頭底下的location 標頭。 例如: location: https://localhost:7260/api/TodoItems/1 。 位置標頭會顯示所建立資源的 URI。

若要測試位置標頭:

  • 在 Swagger 瀏覽器視窗中,選取 [GET /api/TodoItems/{id}],然後選取 [試用]

  • 1 輸入方塊中輸入 id,然後選取 [執行]

    Swagger GET

檢查 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;
}

傳回值

GetTodoItemsGetTodoItem 方法的傳回類型為 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,例如:

如需詳細資訊,請參閱

防止過度張貼

目前範例應用程式會公開整個 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,請使用下列其中一項:

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 應用程式

其他資源

檢視或下載本教學課程的範例程序代碼。 請參閱 如何下載

如需詳細資訊,請參閱以下資源: