教學課程:使用 ASP.NET Core 建立 Web API

Rick AndersonKirk Larkin

本教學課程將教導您使用 ASP.NET Core 建立 Web API 的基本概念。

在本教學課程中,您會了解如何:

  • 建立 Web API 專案。
  • 新增模型類別和資料庫內容。
  • 使用 CRUD 方法 Scaffold 控制器。
  • 設定路由、URL 路徑和傳回值。
  • 使用 Postman 呼叫 Web API。

結束時,您會有一個 Web API,可以管理儲存在資料庫中的「待辦事項」。

概觀

本教學課程會建立以下 API:

API 描述 要求本文 回應本文
GET /api/todoitems 取得所有待辦事項 待辦事項的陣列
GET /api/todoitems/{id} 依識別碼取得項目 待辦事項
POST /api/todoitems 新增記錄 待辦事項 待辦事項
PUT /api/todoitems/{id} 更新現有的項目   待辦事項
DELETE /api/todoitems/{id}     刪除專案    

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

左側方塊代表用戶端。 它會送出要求並接收來自應用程式 (右側繪製的方塊) 的回應。 在應用程式方塊中,三個方塊代表控制器、模型以及資料存取層。 要求進入應用程式的控制器,而在控制器與資料存取層之間進行讀取/寫入作業。 模型會序列化並在回應中傳回至用戶端。

必要條件

建立 Web 專案

  • 在 [檔案] 功能表中,選取 [新增 > Project]。
  • 在 [搜尋] 方塊中輸入 WEB API
  • 選取 ASP.NET Core Web API 範本,然後選取 [下一步]
  • 在 [ 設定您的新專案] 對話方塊 中,將專案命名為 TodoApi ,然後選取 [下一步]
  • 在 [ 其他資訊 ] 對話方塊中,確認 架構.net 6.0 (Preview),然後選取 [ 建立]。

測試專案

專案範本會建立 WeatherForecast 支援 Swagger的 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 顯示。 選取 [立即 > 試用] > 執行。 頁面會顯示:

  • 用來測試 WeatherForecast API 的 捲曲 命令。
  • 用來測試 WeatherForecast API 的 URL。
  • 回應碼、主體和標頭。
  • 具有媒體類型的下拉式清單方塊,以及範例值和架構。

如果 Swagger 頁面未出現,請參閱此 GitHub 問題

Swagger 可用來產生 web Api 的實用檔和說明頁面。 本教學課程著重于建立 web API。 如需 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"
    }
]

更新 launchUrl

Properties\launchSettings.json 中, launchUrl 從更新 "swagger""api/todoitems"

"launchUrl": "api/todoitems",

由於 Swagger 將會被移除,因此上述標記會將啟動的 URL 變更為在下列各節中新增之控制器的 GET 方法。

新增模型類別

「模型」是代表應用程式所管理資料的一組類別。 此應用程式的模型是單一 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 類別來建立。

新增 NuGet 套件

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

新增 TodoCoNtext 資料庫內容

  • 以滑鼠右鍵按一下 Models 資料夾,然後選取 [加入 > 類別]。 將類別命名為 TodoContext,然後按一下 [新增]。
  • 輸入下列程式碼:

    using Microsoft.EntityFrameworkCore;
    using System.Diagnostics.CodeAnalysis;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; } = null!;
        }
    }
    

登錄資料庫內容

在 ASP.NET Core 中,資料庫內容等服務必須向相依性插入 (DI) 容器註冊。 此容器會將服務提供給控制器。

使用下列程式碼更新 程式 .cs

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
//builder.Services.AddSwaggerGen(c =>
//{
//    c.SwaggerDoc("v1", new() { Title = "TodoApi", Version = "v1" });
//});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (builder.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    //app.UseSwagger();
    //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

上述程式碼:

  • 移除 Swagger 呼叫。
  • 移除未使用的指示詞 using
  • 將資料庫內容新增至 DI 容器。
  • 指定資料庫內容將會使用記憶體內部資料庫。

Scaffold 控制器

  • 以滑鼠右鍵按一下 Controllers 資料夾。

  • 選取 [ 加入 > 新的 scaffold 專案]。

  • 選取 [使用 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 範本:

  • 具有 views 的控制器包含 [action] 在路由範本中。
  • API 控制器不包含 [action] 在路由範本中。

[action] 權杖不在路由範本中時,會從路由中排除 動作 名稱。 也就是,不會在相符的路由中使用動作的相關聯方法名稱。

更新 PostTodoItem create 方法

更新中的 return 語句 PostTodoItem ,以使用 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 要求本文取得待辦事項的值。

如需詳細資訊,請參閱使用 Http[Verb] 屬性的屬性路由

CreatedAtAction 方法:

  • 如果成功,則傳回 HTTP 201 狀態碼 。 對於可在伺服器上建立新資源的 HTTP POST 方法,其標準回應是 HTTP 201。
  • Location 標頭新增至回應。 Location標頭會指定新建立之待辦事項的URI 。 如需詳細資訊,請參閱 10.2.2 201 Created (已建立 10.2.2 201)。
  • 參考 GetTodoItem 動作以建立 Location 標頭的 URI。 C# nameof 關鍵字是用來避免在 CreatedAtAction 呼叫中以硬式編碼方式寫入動作名稱。

安裝 HTTP 複寫

本教學課程會使用 HTTP 複寫來測試 web API。

  • 在命令提示字元執行下列命令:

    dotnet tool install -g Microsoft.dotnet-httprepl
    
  • 如果您沒有安裝 .NET 5.0 SDK 或執行時間,請安裝 .net 5.0 運行時間。

測試 PostTodoItem

  • 按 Ctrl+F5 執行應用程式。

  • 開啟新的終端機視窗,然後執行下列命令。 如果您的應用程式使用不同的埠號碼,請將 HTTPrepl 命令中的5001取代為您的埠號碼。

    httprepl https://localhost:5001/api/todoitems
    post -h Content-Type=application/json -c "{"name":"walk dog","isComplete":true}"
    

    以下是命令輸出範例:

    HTTP/1.1 201 Created
    Content-Type: application/json; charset=utf-8
    Date: Tue, 07 Sep 2021 20:39:47 GMT
    Location: https://localhost:5001/api/TodoItems/1
    Server: Kestrel
    Transfer-Encoding: chunked
    
    {
      "id": 1,
      "name": "walk dog",
      "isComplete": true
    }
    

測試位置標頭 URI

若要測試 location 標頭,請將它複製並貼到 HTTPrepl get 命令中。

下列範例假設您仍在 HTTPrepl 會話中。 如果您結束了先前的 HTTPrepl 會話,請 connect 使用 httprepl 下列命令來取代:

connect https://localhost:5001/api/todoitems/1
get

以下是命令輸出範例:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 07 Sep 2021 20:48:10 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
  "id": 1,
  "name": "walk dog",
  "isComplete": true
}

檢查 GET 方法

系統會執行兩個 GET 端點:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

您剛剛看到路由的範例 /api/todoitems/{id} 。 測試 /api/todoitems 路由:

connect https://localhost:5001/api/todoitems
get

以下是命令輸出範例:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 07 Sep 2021 20:59:21 GMT
Server: Kestrel
Transfer-Encoding: chunked

[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]

這次,傳回的 JSON 是一個專案的陣列。

這個應用程式會使用記憶體內部資料庫。 如果應用程式在停止後再啟動,上述 GET 要求將不會傳回任何資料。 如果沒有傳回任何資料,請將資料 POST 到應用程式。

傳送和 URL 路徑

[HttpGet]屬性代表回應 HTTP GET 要求的方法。 每個方法的 URL 路徑的建構方式如下:

  • 一開始在控制器的 Route 屬性中使用範本字串:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • 以控制器的名稱取代 [controller],也就是將控制器類別名稱減去 "Controller" 字尾。 在此範例中,控制器類別名稱是 TodoItems Controller,因此控制器名稱是 "TodoItems"。 ASP.NET Core 路由不區分大小寫。

  • 如果 [HttpGet] 屬性具有路由範本 (例如 [HttpGet("products")]),請將其附加到路徑。 此範例不使用範本。 如需詳細資訊,請參閱使用 Http[Verb] 屬性的屬性路由

在下列 GetTodoItem 方法中,"{id}" 是待辦事項唯一識別碼的預留位置變數。 當叫 GetTodoItem 用時,會將 "{id}" URL 中的值提供給方法的 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;
}

傳回值

和方法的傳回型 GetTodoItemsGetTodoItemActionResult <T> 類型。 ASP.NET Core 會自動將物件序列化為 JSON,並將 JSON 寫入至回應訊息的本文。 如果沒有任何未處理的例外狀況,則此傳回型別的回應碼為 200 OK。 未處理的例外狀況會轉譯成 5xx 錯誤。

ActionResult 傳回型別可代表各種 HTTP 狀態碼。 例如,GetTodoItem 可傳回兩個不同的狀態值:

  • 如果沒有任何專案符合要求的識別碼,方法會傳回 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 在下一節中遇到錯誤,請呼叫 GET 以確保資料庫中有專案。

測試 PutTodoItem 方法

這個範例會使用必須在每次啟動應用程式時初始化的記憶體內部資料庫。 資料庫中必須有項目,您才能進行 PUT 呼叫。 在進行 PUT 呼叫之前,請先呼叫 GET 以確保資料庫中有專案。

更新識別碼為1的待辦事項,並將其名稱設定為 "feed fish"

connect https://localhost:5001/api/todoitems/1
put -h Content-Type=application/json -c "{"id":1,"name":"feed fish","isComplete":true}"

以下是命令輸出範例:

HTTP/1.1 204 No Content
Date: Tue, 07 Sep 2021 21:20:47 GMT
Server: Kestrel

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 方法

刪除 Id = 1 的待辦事項專案:

connect https://localhost:5001/api/todoitems/1
delete

以下是命令輸出範例:

HTTP/1.1 204 No Content
Date: Tue, 07 Sep 2021 21:43:00 GMT
Server: Kestrel

防止過度張貼

範例應用程式目前會公開整個 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
        [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);
        }
        // PUT: api/TodoItems/5
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
        {
            if (id != todoItemDTO.Id)
            {
                return BadRequest();
            }

            var todoItem = await _context.TodoItems.FindAsync(id);
            if (todoItem == null)
            {
                return NotFound();
            }

            todoItem.Name = todoItemDTO.Name;
            todoItem.IsComplete = todoItemDTO.IsComplete;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
            {
                return NotFound();
            }

            return NoContent();
        }
        // POST: api/TodoItems
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
        {
            var todoItem = new TodoItem
            {
                IsComplete = todoItemDTO.IsComplete,
                Name = todoItemDTO.Name
            };

            _context.TodoItems.Add(todoItem);
            await _context.SaveChangesAsync();

            return CreatedAtAction(
                nameof(GetTodoItem),
                new { id = todoItem.Id },
                ItemToDTO(todoItem));
        }

        // 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 專案。
  • 新增模型類別和資料庫內容。
  • 使用 CRUD 方法 Scaffold 控制器。
  • 設定路由、URL 路徑和傳回值。
  • 使用 Postman 呼叫 Web API。

結束時,您會有一個 Web API,可以管理儲存在資料庫中的「待辦事項」。

概觀

本教學課程會建立以下 API:

API 描述 要求本文 回應本文
GET /api/todoitems 取得所有待辦事項 待辦事項的陣列
GET /api/todoitems/{id} 依識別碼取得項目 待辦事項
POST /api/todoitems 新增記錄 待辦事項 待辦事項
PUT /api/todoitems/{id} 更新現有的項目   待辦事項
DELETE /api/todoitems/{id}     刪除專案    

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

左側方塊代表用戶端。 它會送出要求並接收來自應用程式 (右側繪製的方塊) 的回應。 在應用程式方塊中,三個方塊代表控制器、模型以及資料存取層。 要求進入應用程式的控制器,而在控制器與資料存取層之間進行讀取/寫入作業。 模型會序列化並在回應中傳回至用戶端。

必要條件

建立 Web 專案

  • 在 [檔案] 功能表中,選取 [新增 > Project]。
  • 選取 ASP.NET Core Web API 範本,然後按 [下一步]
  • 將專案命名為 TodoApi,然後按一下 [建立]。
  • 在 [建立新的 ASP.NET Core Web 應用程式] 對話方塊中,確認已選取 [ .net Core ] 和 [ ASP.NET Core 5.0 ]。 選取 API 範本,然後按一下 [建立]。

VS 新增專案對話方塊

測試專案

專案範本會建立 WeatherForecast 支援 Swagger的 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 啟動:

  • IIS Express web 伺服器。
  • 預設瀏覽器並流覽至 https://localhost:<port>/swagger/index.html ,其中 <port> 是隨機播放的埠號碼。

Swagger 頁面隨即 /swagger/index.html 顯示。 選取 [立即 > 試用] > 執行。 頁面會顯示:

  • 用來測試 WeatherForecast API 的 捲曲 命令。
  • 用來測試 WeatherForecast API 的 URL。
  • 回應碼、主體和標頭。
  • 具有媒體類型的下拉式清單方塊,以及範例值和架構。

如果 Swagger 頁面未出現,請參閱此 GitHub 問題

Swagger 可用來產生 web Api 的實用檔和說明頁面。 本教學課程著重于建立 web API。 如需 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"
    }
]

更新 launchUrl

Properties\launchSettings.json 中, launchUrl 從更新 "swagger""api/todoitems"

"launchUrl": "api/todoitems",

由於 Swagger 將會被移除,因此上述標記會將啟動的 URL 變更為在下列各節中新增之控制器的 GET 方法。

新增模型類別

「模型」是代表應用程式所管理資料的一組類別。 此應用程式的模型是單一 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 類別來建立。

新增 NuGet 套件

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

NuGet 套件管理員

新增 TodoCoNtext 資料庫內容

  • 以滑鼠右鍵按一下 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; }
        }
    }
    

登錄資料庫內容

在 ASP.NET Core 中,資料庫內容等服務必須向相依性插入 (DI) 容器註冊。 此容器會將服務提供給控制器。

使用下列程式碼來更新 啟動 .cs

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddDbContext<TodoContext>(opt =>
                                               opt.UseInMemoryDatabase("TodoList"));
            //services.AddSwaggerGen(c =>
            //{
            //    c.SwaggerDoc("v1", new OpenApiInfo { Title = "TodoApi", Version = "v1" });
            //});
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                //app.UseSwagger();
                //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
            }

            app.UseHttpsRedirection();
            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

上述程式碼:

  • 移除 Swagger 呼叫。
  • 移除未使用的 using 宣告。
  • 將資料庫內容新增至 DI 容器。
  • 指定資料庫內容將會使用記憶體內部資料庫。

Scaffold 控制器

  • 以滑鼠右鍵按一下 Controllers 資料夾。

  • 選取 [ 加入 > 新的 scaffold 專案]。

  • 選取 [使用 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 範本:

  • 具有 views 的控制器包含 [action] 在路由範本中。
  • API 控制器不包含 [action] 在路由範本中。

[action] 權杖不在路由範本中時,會從路由中排除 動作 名稱。 也就是,不會在相符的路由中使用動作的相關聯方法名稱。

更新 PostTodoItem create 方法

更新中的 return 語句 PostTodoItem ,以使用 nameof 運算子:

// POST: api/TodoItems
[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 要求本文取得待辦事項的值。

如需詳細資訊,請參閱使用 Http[Verb] 屬性的屬性路由

CreatedAtAction 方法:

  • 如果成功,則傳回 HTTP 201 狀態碼 。 對於可在伺服器上建立新資源的 HTTP POST 方法,其標準回應是 HTTP 201。
  • Location 標頭新增至回應。 Location標頭會指定新建立之待辦事項的URI 。 如需詳細資訊,請參閱 10.2.2 201 Created (已建立 10.2.2 201)。
  • 參考 GetTodoItem 動作以建立 Location 標頭的 URI。 C# nameof 關鍵字是用來避免在 CreatedAtAction 呼叫中以硬式編碼方式寫入動作名稱。

安裝 Postman

本教學課程使用 Postman 來測試 Web API。

  • 安裝 Postman
  • 啟動 Web 應用程式。
  • 啟動 Postman。
  • 停用 [SSL certificate verification] (SSL 憑證驗證)
    • 從 > [檔案 設定 (一般] 索引標籤) ,停用 SSL 憑證驗證

      警告

      在測試控制器之後,請重新啟用 [SSL certificate verification] (SSL 憑證驗證)。

使用 Postman 測試 PostTodoItem

  • 建立新的要求。

  • 將 HTTP 方法設為 POST

  • 將 URI 設定為 https://localhost:<port>/api/todoitems 。 例如: https://localhost:5001/api/todoitems

  • 選取 [Body] (本文) 索引標籤。

  • 選取 [原始] 選項按鈕。

  • 將類型設定為 JSON (application/json)

  • 在要求本文中,針對待辦項目輸入 JSON:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • 選取 [傳送]。

    Postman 與建立要求

測試位置標頭 URI

您可以在瀏覽器中測試位置標頭 URI。 複製位置標頭 URI,並將其貼到瀏覽器中。

若要在 Postman 中進行測試:

  • 在 [回應] 窗格中選取 [標頭] 索引標籤。

  • 複製 [位置] 標頭值:

    Postman 主控台的 [標頭] 索引標籤

  • 將 HTTP 方法設為 GET

  • 將 URI 設定為 https://localhost:<port>/api/todoitems/1 。 例如: https://localhost:5001/api/todoitems/1

  • 選取 [傳送]。

檢查 GET 方法

系統會執行兩個 GET 端點:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

從瀏覽器或 Postman 呼叫這兩個端點來測試應用程式。 例如:

  • https://localhost:5001/api/todoitems
  • https://localhost:5001/api/todoitems/1

GetTodoItems 的呼叫會產生類似下列的回應:

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

使用 Postman 測試 Get

  • 建立新的要求。
  • 將 HTTP 方法設定為 GET
  • 將要求 URI 設定為 https://localhost:<port>/api/todoitems 。 例如: https://localhost:5001/api/todoitems
  • 在 Postman 中,設定 [Two pane view] (雙窗格檢視)。
  • 選取 [傳送]。

這個應用程式會使用記憶體內部資料庫。 如果應用程式在停止後再啟動,上述 GET 要求將不會傳回任何資料。 如果沒有傳回任何資料,請將資料 POST 到應用程式。

傳送和 URL 路徑

[HttpGet]屬性代表回應 HTTP GET 要求的方法。 每個方法的 URL 路徑的建構方式如下:

  • 一開始在控制器的 Route 屬性中使用範本字串:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • 以控制器的名稱取代 [controller],也就是將控制器類別名稱減去 "Controller" 字尾。 在此範例中,控制器類別名稱是 TodoItems Controller,因此控制器名稱是 "TodoItems"。 ASP.NET Core 路由不區分大小寫。

  • 如果 [HttpGet] 屬性具有路由範本 (例如 [HttpGet("products")]),請將其附加到路徑。 此範例不使用範本。 如需詳細資訊,請參閱使用 Http[Verb] 屬性的屬性路由

在下列 GetTodoItem 方法中,"{id}" 是待辦事項唯一識別碼的預留位置變數。 當叫 GetTodoItem 用時,會將 "{id}" URL 中的值提供給方法的 id 參數。

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

傳回值

和方法的傳回型 GetTodoItemsGetTodoItemActionResult <T> 類型。 ASP.NET Core 會自動將物件序列化為 JSON,並將 JSON 寫入至回應訊息的本文。 如果沒有任何未處理的例外狀況,則此傳回型別的回應碼為 200 OK。 未處理的例外狀況會轉譯成 5xx 錯誤。

ActionResult 傳回型別可代表各種 HTTP 狀態碼。 例如,GetTodoItem 可傳回兩個不同的狀態值:

  • 如果沒有任何專案符合要求的識別碼,方法會傳回 404 狀態 NotFound 錯誤碼。
  • 否則,方法會傳回 200 與 JSON 回應本文。 傳回 item 會導致 HTTP 200 回應。

PutTodoItem 方法

檢查 PutTodoItem 方法:

// PUT: api/TodoItems/5
[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 時發生錯誤,請呼叫 GET 以確保資料庫中有項目。

測試 PutTodoItem 方法

這個範例會使用必須在每次啟動應用程式時初始化的記憶體內部資料庫。 資料庫中必須有項目,您才能進行 PUT 呼叫。 在進行 PUT 呼叫之前,請先呼叫 GET 以確保資料庫中有專案。

更新識別碼為1的待辦事項,並將其名稱設定為 "feed fish"

  {
    "Id":1,
    "name":"feed fish",
    "isComplete":true
  }

下圖顯示 Postman 更新:

顯示「204 (沒有內容) 回應」的 Postman 主控台

DeleteTodoItem 方法

檢查 DeleteTodoItem 方法:

// 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();
}

測試 DeleteTodoItem 方法

使用 Postman 刪除待辦事項:

  • 將方法設定為 DELETE
  • 設定要刪除之物件的 URI (例如 https://localhost:5001/api/todoitems/1) 。
  • 選取 [傳送]。

防止過度張貼

範例應用程式目前會公開整個 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 模型:

public class TodoItemDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

更新 TodoItemsController 以使用 TodoItemDTO

// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
    return await _context.TodoItems
        .Select(x => ItemToDTO(x))
        .ToListAsync();
}

[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);
}

[HttpPut("{id}")]
public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
{
    if (id != todoItemDTO.Id)
    {
        return BadRequest();
    }

    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    todoItem.Name = todoItemDTO.Name;
    todoItem.IsComplete = todoItemDTO.IsComplete;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
    {
        return NotFound();
    }

    return NoContent();
}

[HttpPost]
public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
{
    var todoItem = new TodoItem
    {
        IsComplete = todoItemDTO.IsComplete,
        Name = todoItemDTO.Name
    };

    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    return CreatedAtAction(
        nameof(GetTodoItem),
        new { id = todoItem.Id },
        ItemToDTO(todoItem));
}

[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) =>
     _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 專案。
  • 新增模型類別和資料庫內容。
  • 使用 CRUD 方法 Scaffold 控制器。
  • 設定路由、URL 路徑和傳回值。
  • 使用 Postman 呼叫 Web API。

結束時,您會有一個 Web API,可以管理儲存在資料庫中的「待辦事項」。

概觀

本教學課程會建立以下 API:

API 描述 要求本文 回應本文
GET /api/todoitems 取得所有待辦事項 待辦事項的陣列
GET /api/todoitems/{id} 依識別碼取得項目 待辦事項
POST /api/todoitems 新增記錄 待辦事項 待辦事項
PUT /api/todoitems/{id} 更新現有的項目   待辦事項
DELETE /api/todoitems/{id}     刪除專案    

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

左側方塊代表用戶端。 它會送出要求並接收來自應用程式 (右側繪製的方塊) 的回應。 在應用程式方塊中,三個方塊代表控制器、模型以及資料存取層。 要求進入應用程式的控制器,而在控制器與資料存取層之間進行讀取/寫入作業。 模型會序列化並在回應中傳回至用戶端。

必要條件

建立 Web 專案

  • 在 [檔案] 功能表中,選取 [新增 > Project]。
  • 選取 ASP.NET Core Web 應用程式 範本,然後按一下 [下一步]。
  • 將專案命名為 TodoApi,然後按一下 [建立]。
  • 在 [建立新的 ASP.NET Core Web 應用程式] 對話方塊中,確認已選取 [ .net Core ] 和 [ ASP.NET Core 3.1 ]。 選取 API 範本,然後按一下 [建立]。

VS 新增專案對話方塊

測試 API

專案範本會建立 WeatherForecast API。 從瀏覽器呼叫 Get 方法來測試應用程式。

按 Ctrl+F5 執行應用程式。 Visual Studio 會啟動瀏覽器並巡覽至 https://localhost:<port>/WeatherForecast,其中 <port> 是隨機選擇的通訊埠編號。

如果出現對話方塊詢問您是否應該信任 IIS Express 憑證,請選取 [是]。 在接著出現的 [安全性警告] 對話方塊中,選取 [是]。

系統會傳回與下列類似的 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,然後選取 [新增]。

  • 使用下列程式碼取代範本程式碼:

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 類別來建立。

新增 NuGet 套件

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

NuGet 套件管理員

新增 TodoCoNtext 資料庫內容

  • 以滑鼠右鍵按一下 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; }
        }
    }
    

登錄資料庫內容

在 ASP.NET Core 中,資料庫內容等服務必須向相依性插入 (DI) 容器註冊。 此容器會將服務提供給控制器。

使用下列醒目提示的程式碼更新 Startup.cs

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TodoContext>(opt =>
               opt.UseInMemoryDatabase("TodoList"));
            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

上述程式碼:

  • 移除未使用的 using 宣告。
  • 將資料庫內容新增至 DI 容器。
  • 指定資料庫內容將會使用記憶體內部資料庫。

Scaffold 控制器

  • 以滑鼠右鍵按一下 Controllers 資料夾。

  • 選取 [ 加入 > 新的 scaffold 專案]。

  • 選取 [使用 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 範本:

  • 具有 views 的控制器包含 [action] 在路由範本中。
  • API 控制器不包含 [action] 在路由範本中。

[action] 權杖不在路由範本中時,會從路由中排除 動作 名稱。 也就是,不會在相符的路由中使用動作的相關聯方法名稱。

檢查 PostTodoItem 建立方法

取代 PostTodoItem 中的 return 陳述式,以使用 nameof 運算子:

// POST: api/TodoItems
[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 要求本文取得待辦事項的值。

如需詳細資訊,請參閱使用 Http[Verb] 屬性的屬性路由

CreatedAtAction 方法:

  • 成功時會傳回 HTTP 201 狀態碼。 對於可在伺服器上建立新資源的 HTTP POST 方法,其標準回應是 HTTP 201。
  • Location 標頭新增至回應。 Location標頭會指定新建立之待辦事項的URI 。 如需詳細資訊,請參閱 10.2.2 201 Created (已建立 10.2.2 201)。
  • 參考 GetTodoItem 動作以建立 Location 標頭的 URI。 C# nameof 關鍵字是用來避免在 CreatedAtAction 呼叫中以硬式編碼方式寫入動作名稱。

安裝 Postman

本教學課程使用 Postman 來測試 Web API。

  • 安裝 Postman
  • 啟動 Web 應用程式。
  • 啟動 Postman。
  • 停用 [SSL certificate verification] (SSL 憑證驗證)
    • 從 > [檔案 設定 (一般] 索引標籤) ,停用 SSL 憑證驗證

      警告

      在測試控制器之後,請重新啟用 [SSL certificate verification] (SSL 憑證驗證)。

使用 Postman 測試 PostTodoItem

  • 建立新的要求。

  • 將 HTTP 方法設為 POST

  • 將 URI 設定為 https://localhost:<port>/api/todoitems 。 例如: https://localhost:5001/api/todoitems

  • 選取 [Body] (本文) 索引標籤。

  • 選取 [原始] 選項按鈕。

  • 將類型設定為 JSON (application/json)

  • 在要求本文中,針對待辦項目輸入 JSON:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • 選取 [傳送]。

    Postman 與建立要求

使用 Postman 測試 location 標頭 URI

  • 在 [回應] 窗格中選取 [標頭] 索引標籤。

  • 複製 [位置] 標頭值:

    Postman 主控台的 [標頭] 索引標籤

  • 將 HTTP 方法設為 GET

  • 將 URI 設定為 https://localhost:<port>/api/todoitems/1 。 例如: https://localhost:5001/api/todoitems/1

  • 選取 [傳送]。

檢查 GET 方法

這些方法會實作兩個 GET 端點:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

從瀏覽器或 Postman 呼叫這兩個端點來測試應用程式。 例如:

  • https://localhost:5001/api/todoitems
  • https://localhost:5001/api/todoitems/1

GetTodoItems 的呼叫會產生類似下列的回應:

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

使用 Postman 測試 Get

  • 建立新的要求。
  • 將 HTTP 方法設定為 GET
  • 將要求 URI 設定為 https://localhost:<port>/api/todoitems 。 例如: https://localhost:5001/api/todoitems
  • 在 Postman 中,設定 [Two pane view] (雙窗格檢視)。
  • 選取 [傳送]。

這個應用程式會使用記憶體內部資料庫。 如果應用程式在停止後再啟動,上述 GET 要求將不會傳回任何資料。 如果沒有傳回任何資料,請將資料 POST 到應用程式。

傳送和 URL 路徑

[HttpGet]屬性代表回應 HTTP GET 要求的方法。 每個方法的 URL 路徑的建構方式如下:

  • 一開始在控制器的 Route 屬性中使用範本字串:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • 以控制器的名稱取代 [controller],也就是將控制器類別名稱減去 "Controller" 字尾。 在此範例中,控制器類別名稱是 TodoItems Controller,因此控制器名稱是 "TodoItems"。 ASP.NET Core 路由不區分大小寫。

  • 如果 [HttpGet] 屬性具有路由範本 (例如 [HttpGet("products")]),請將其附加到路徑。 此範例不使用範本。 如需詳細資訊,請參閱使用 Http[Verb] 屬性的屬性路由

在下列 GetTodoItem 方法中,"{id}" 是待辦事項唯一識別碼的預留位置變數。 當叫 GetTodoItem 用時,會將 "{id}" URL 中的值提供給方法的 id 參數。

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

傳回值

和方法的傳回型 GetTodoItemsGetTodoItemActionResult <T> 類型。 ASP.NET Core 會自動將物件序列化為 JSON,並將 JSON 寫入至回應訊息的本文。 此傳回型別的回應碼為 200,假設沒有任何未處理的例外狀況。 未處理的例外狀況會轉譯成 5xx 錯誤。

ActionResult 傳回型別可代表各種 HTTP 狀態碼。 例如,GetTodoItem 可傳回兩個不同的狀態值:

  • 如果沒有任何專案符合要求的識別碼,方法會傳回 404 NotFound 錯誤碼。
  • 否則,方法會傳回 200 與 JSON 回應本文。 傳回 item 會導致 HTTP 200 回應。

PutTodoItem 方法

檢查 PutTodoItem 方法:

// PUT: api/TodoItems/5
[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 時發生錯誤,請呼叫 GET 以確保資料庫中有項目。

測試 PutTodoItem 方法

這個範例會使用必須在每次啟動應用程式時初始化的記憶體內部資料庫。 資料庫中必須有項目,您才能進行 PUT 呼叫。 在進行 PUT 呼叫之前,請先呼叫 GET 以確保資料庫中有專案。

更新識別碼為1的待辦事項,並將其名稱設定為「摘要魚」:

  {
    "id":1,
    "name":"feed fish",
    "isComplete":true
  }

下圖顯示 Postman 更新:

顯示「204 (沒有內容) 回應」的 Postman 主控台

DeleteTodoItem 方法

檢查 DeleteTodoItem 方法:

// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<ActionResult<TodoItem>> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return todoItem;
}

測試 DeleteTodoItem 方法

使用 Postman 刪除待辦事項:

  • 將方法設定為 DELETE
  • 設定要刪除之物件的 URI (例如 https://localhost:5001/api/todoitems/1) 。
  • 選取 [傳送]。

防止過度張貼

範例應用程式目前會公開整個 TodoItem 物件。 生產環境應用程式通常會限制使用模型子集來輸入和傳回的資料。 這項功能有許多原因,而且安全性是主要的原因。 模型的子集通常稱為資料傳輸物件 (DTO) 、輸入模型或視圖模型。 此文章中使用 DTO

DTO 可以用來:

  • 防止過度張貼。
  • 隱藏用戶端不應該看到的屬性。
  • 略過某些屬性,以減少承載大小。
  • 壓平合併包含嵌套物件的物件圖形。 簡維物件圖形對於用戶端來說可能更方便。

若要示範 DTO 方法,請更新 TodoItem 類別以包含秘密欄位:

public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
    public string Secret { get; set; }
}

此應用程式必須隱藏秘密欄位,但系統管理應用程式可以選擇將它公開。

確認您可以張貼並取得秘密欄位。

建立 DTO 模型:

public class TodoItemDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

更新 TodoItemsController 以使用 TodoItemDTO

    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
    {
        return await _context.TodoItems
            .Select(x => ItemToDTO(x))
            .ToListAsync();
    }

    [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);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
    {
        if (id != todoItemDTO.Id)
        {
            return BadRequest();
        }

        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        todoItem.Name = todoItemDTO.Name;
        todoItem.IsComplete = todoItemDTO.IsComplete;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
        {
            return NotFound();
        }

        return NoContent();
    }

    [HttpPost]
    public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
    {
        var todoItem = new TodoItem
        {
            IsComplete = todoItemDTO.IsComplete,
            Name = todoItemDTO.Name
        };

        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();

        return CreatedAtAction(
            nameof(GetTodoItem),
            new { id = todoItem.Id },
            ItemToDTO(todoItem));
    }

    [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) =>
         _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

ASP.NET Core Identity將使用者介面 (UI) 登入功能新增至 ASP.NET Core web 應用程式。 若要保護 web Api 和 Spa,請使用下列其中一項:

IdentityServer4 是 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 架構。 IdentityServer4 可啟用下列安全性功能:

  • 驗證即服務 (AaaS)
  • 單一登入/關閉 (SSO) 超過多個應用程式類型
  • Api 的存取控制
  • 同盟閘道

如需詳細資訊,請參閱 歡迎使用 Identity Server4

其他資源

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

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