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

作者 :Rick AndersonKirk Larkin

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

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

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

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

概觀

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

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

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

The client is represented by a box on the left. It submits a request and receives a response from the application, a box drawn on the right. Within the application box, three boxes represent the controller, the model, and the data access layer. The request comes into the application's controller, and read/write operations occur between the controller and the data access layer. The model is serialized and returned to the client in the response.

必要條件

建立 Web 專案

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

注意

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

測試專案

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

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

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

This project is configured to use SSL. To avoid SSL warnings in the browser you can choose to trust the self-signed certificate that IIS Express has generated. Would you like to trust the IIS Express SSL certificate?

如果您信任 IIS Express SSL 憑證,請選取 [是]

此時會顯示下列對話方塊:

Security warning dialog

若您同意信任開發憑證,請選取 [是]

如需信任 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 產生有用的檔和說明頁面。 本教學課程著重于建立 Web API。 如需Swagger 的詳細資訊,請參閱使用 Swagger / OpenAPI ASP.NET Core Web API 檔

複製並貼上瀏覽器中 的要求 URLhttps://localhost:<port>/weatherforecast

JS會傳回類似下列範例的 ON:

[
    {
        "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中,從 "swagger" 更新 launchUrl"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) 容器註冊。 此容器會將服務提供給控制器。

使用下列程式碼更新 Program.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 資料夾。

  • 選取[新增>Scaffolded 專案]。

  • 選取 [使用 Entity Framework 執行動作的 API 控制器],然後選取 [新增]

  • 在 [使用 Entity Framework 執行動作的 API 控制器] 對話方塊中:

    • Model 類別中選取 TodoItem (TodoApi.Models)
    • Data 內容類別別中選取 TodoCoNtext (TodoApi.Models)
    • 選取 [新增]。

    如果 Scaffolding 作業失敗,請選取 [ 新增 ] 嘗試第二次 Scaffolding。

產生的程式碼:

  • 使用 [ApiController] 屬性標記 類別。 這個屬性表示控制器會回應 Web API 要求。 如需屬性啟用的特定行為相關資訊,請參閱使用 ASP.NET Core 建立 Web API
  • 使用 DI 將資料庫內容 (TodoContext) 插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

下列專案的 ASP.NET Core範本:

  • 包含路由範本中檢視 [action] 的控制器。
  • API 控制器不包含 [action] 在路由範本中。

[action]當令牌不在路由範本中時,動作名稱會從路由中排除。 也就是說,動作的相關聯方法名稱不會用於比對路由中。

更新 PostTodoItem create 方法

更新 中的 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 要求本文取得待辦事項的值。

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

CreatedAtAction 方法:

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

安裝 HTTP-repl

本教學課程使用 HTTP-repl 來測試 Web API。

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

    dotnet tool install -g Microsoft.dotnet-httprepl
    
  • 如果您沒有安裝 .NET 6.0 SDK 或執行時間,請安裝 .NET 6.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

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

下列範例假設您仍在 HTTPrepl 會話中。 如果您結束先前的 HTTPrepl 會話,請在下列命令中以 取代 connecthttprepl

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
  }
]

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

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

傳送和 URL 路徑

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

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

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

  • 如果 [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 傳回型別為ActionResult < T > 類型GetTodoItem ASP.NET Core會自動將物件序列化為JS ON,並將 ON 寫入 JS 回應訊息的本文。 此傳回類型的回應碼為 200 OK,假設沒有未處理的例外狀況。 未處理的例外狀況會轉譯成 5xx 錯誤。

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

  • 如果沒有專案符合要求的識別碼,此方法會傳回404 狀態NotFound 錯誤碼。
  • 否則,方法會傳回具有 ON 回應本文的 JS 200。 傳回 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 呼叫。 呼叫 GET 以確保資料庫中有專案,再進行 PUT 呼叫。

更新識別碼 = 1 的 to-do 專案,並將其名稱設定為 "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 方法

刪除識別碼 = 1 的 to-do 專案:

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

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

The client is represented by a box on the left. It submits a request and receives a response from the application, a box drawn on the right. Within the application box, three boxes represent the controller, the model, and the data access layer. The request comes into the application's controller, and read/write operations occur between the controller and the data access layer. The model is serialized and returned to the client in the response.

必要條件

建立 Web 專案

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

VS new project dialog

注意

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

測試專案

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

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

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

This project is configured to use SSL. To avoid SSL warnings in the browser you can choose to trust the self-signed certificate that IIS Express has generated. Would you like to trust the IIS Express SSL certificate?

如果您信任 IIS Express SSL 憑證,請選取 [是]

此時會顯示下列對話方塊:

Security warning dialog

若您同意信任開發憑證,請選取 [是]

如需信任 Firefox 瀏覽器的資訊,請參閱 Firefox SEC_ERROR_INADEQUATE_KEY_USAGE憑證錯誤

Visual Studio啟動:

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

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

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

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

Swagger 可用來為 Web API 產生有用的檔和說明頁面。 本教學課程著重于建立 Web API。 如需Swagger 的詳細資訊,請參閱使用 Swagger / OpenAPI ASP.NET Core Web API 檔

複製並貼上瀏覽器中 的要求 URLhttps://localhost:<port>/weatherforecast

JS會傳回類似下列的 ON:

[
    {
        "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中,從 "swagger" 更新 launchUrl"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 Package Manager

新增 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.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 資料夾。

  • 選取[新增>Scaffolded 專案]。

  • 選取 [使用 Entity Framework 執行動作的 API 控制器],然後選取 [新增]

  • 在 [使用 Entity Framework 執行動作的 API 控制器] 對話方塊中:

    • Model 類別中選取 TodoItem (TodoApi.Models)
    • Data 內容類別別中選取 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 運算子:

// 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標頭會指定新建立的 To-do 專案的URI。 如需詳細資訊,請參閱 10.2.2 201 Created (已建立 10.2.2 201)。
  • 參考 GetTodoItem 動作以建立 Location 標頭的 URI。 C# nameof 關鍵字是用來避免在 CreatedAtAction 呼叫中以硬式編碼方式寫入動作名稱。

安裝 Postman

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

  • 安裝 Postman
  • 啟動 Web 應用程式。
  • 啟動 Postman。
  • 停用 SSL 憑證驗證
    • postman for Windows:選取 [檔案>設定 ([一般] 索引標籤) ,停用SSL 憑證驗證
    • postman for macOS:選取Postman>設定 ([一般] 索引標籤) ,停用SSL 憑證驗證

      警告

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

使用 Postman 測試 PostTodoItem

  • 建立新的要求。

  • 將 HTTP 方法設為 POST

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

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

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

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

  • 在要求本文中,針對 to-do 專案輸入 JS ON:

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

    Postman with create request

測試位置標頭 URI

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

若要在 Postman 中測試:

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

  • 複製 [位置] 標頭值:

    Headers tab of the Postman console

  • 將 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" 字尾。 在此範例中,控制器類別名稱是 TodoItemsController,因此控制器名稱是 "TodoItems"。 ASP.NET Core 路由不區分大小寫。

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

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

傳回值

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

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

  • 如果沒有專案符合要求的識別碼,此方法會傳回404 狀態NotFound 錯誤碼。
  • 否則,方法會傳回具有 ON 回應本文的 JS 200。 傳回 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 呼叫。 呼叫 GET 以確保資料庫中有專案,再進行 PUT 呼叫。

更新識別碼 = 1 的 to-do 專案,並將其名稱設定為 "feed fish"

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

下圖顯示 Postman 更新:

Postman console showing 204 (No Content) response

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

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

The client is represented by a box on the left. It submits a request and receives a response from the application, a box drawn on the right. Within the application box, three boxes represent the controller, the model, and the data access layer. The request comes into the application's controller, and read/write operations occur between the controller and the data access layer. The model is serialized and returned to the client in the response.

必要條件

建立 Web 專案

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

VS new project dialog

注意

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

測試 API

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

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

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

JS會傳回類似下列的 ON:

[
    {
        "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.InMemory
  • 選取左窗格中的 [Microsoft.EntityFrameworkCore.InMemory ]。
  • 選取右窗格中的 [Project] 核取方塊,然後選取 [安裝]。

NuGet Package Manager

新增 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 資料夾。

  • 選取[新增>Scaffolded 專案]。

  • 選取 [使用 Entity Framework 執行動作的 API 控制器],然後選取 [新增]

  • 在 [使用 Entity Framework 執行動作的 API 控制器] 對話方塊中:

    • Model 類別中選取 TodoItem (TodoApi.Models)
    • Data 內容類別別中選取 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 運算子:

// 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標頭會指定新建立的 To-do 專案的URI。 如需詳細資訊,請參閱 10.2.2 201 Created (已建立 10.2.2 201)。
  • 參考 GetTodoItem 動作以建立 Location 標頭的 URI。 C# nameof 關鍵字是用來避免在 CreatedAtAction 呼叫中以硬式編碼方式寫入動作名稱。

安裝 Postman

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

  • 安裝 Postman
  • 啟動 Web 應用程式。
  • 啟動 Postman。
  • 停用 SSL 憑證驗證
    • postman for Windows:postman for Windows File>設定 ([一般] 索引標籤) 停用SSL 憑證驗證
    • postman for macOS:postman for Windows Postman>設定 ([一般] 索引標籤) 停用SSL 憑證驗證

      警告

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

使用 Postman 測試 PostTodoItem

  • 建立新的要求。

  • 將 HTTP 方法設為 POST

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

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

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

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

  • 在要求本文中,針對 To-do 專案輸入 JS ON:

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

    Postman with create request

使用 Postman 測試位置標頭 URI

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

  • 複製 [位置] 標頭值:

    Headers tab of the Postman console

  • 將 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" 字尾。 在此範例中,控制器類別名稱是 TodoItemsController,因此控制器名稱是 "TodoItems"。 ASP.NET Core 路由不區分大小寫。

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

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

傳回值

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

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

  • 如果沒有專案符合要求的識別碼,此方法會傳回 404 NotFound 錯誤碼。
  • 否則,方法會傳 JS 回具有 ON 回應本文的 200。 傳回 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 呼叫。 呼叫 GET 以確保資料庫中有一個專案,再進行 PUT 呼叫。

更新識別碼 = 1 的 To-do 專案,並將其名稱設定為 「feed fish」:

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

下圖顯示 Postman 更新:

Postman console showing 204 (No Content) response

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

IdentityASP.NET Core會將使用者介面 (UI) 登入功能新增至 ASP.NET Core Web 應用程式。 若要保護 Web API 和 SPA 的安全,請使用下列其中一項:

Duende Identity Server 是適用于 ASP.NET Core 的 OpenID 連線 和 OAuth 2.0 架構。 Duende Identity Server 會啟用下列安全性功能:

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

重要

Duende Software 可能會要求您支付 Duende Identity Server 生產環境使用的授權費用。 如需詳細資訊,請參閱從 ASP.NET Core 5.0 移轉至 6.0

如需詳細資訊,請參閱 Duende Identity Server 檔 (Duende Software 網站)

發佈至 Azure

如需部署至 Azure 的資訊,請參閱快速入門:部署 ASP.NET Web 應用程式

其他資源

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

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