ASP.NET Core 中文文檔 第二章 指南(2)用 Visual Studio 和 ASP.NET Core MVC 創建首個 Web API


原文:Building Your First Web API with ASP.NET Core MVC and Visual Studio
作者:Mike WassonRick Anderson
翻譯:謝煬(kiler)
校對:何鎮汐劉怡(AlexLEWIS)后知后覺

HTTP 協議不僅僅提供網頁服務。它也是一個構建公開服務和數據 API 的強大平台。HTTP 協議是簡單、靈活、無處不在的。幾乎你能想到的任何平台上都有 HTTP 支持,所以 HTTP 服務能夠發送到多種客戶端, 包括瀏覽器,移動設備和傳統的桌面應用程序。

在本教程中,你將創建一個簡單的 Web API 來管理一個 "to-do" 列表。在本教程中你無需編寫任何 UI 代碼。

ASP.NET Core 已經內置了用 MVC 架構 構建 Web API 的支持。統一了兩個框架使得它易於構建應用程序,包括用戶界面(HTML)和 API,因為現在它們共享相同的代碼庫和管道。

注意
如果你想把一個老的 Web API 應用程序遷移到 ASP.NET Core, 參考從 ASP.NET Web API 遷移


總覽

這是你需要創建的 API :

API 描述 請求正文 響應正文
GET /api/todo 獲取所有的to-do items Array of to-do items
GET /api/todo/{id} 通過ID獲取item To-do item
POST /api/todo 添加一個新的item To-do item To-do item
PUT /api/todo/{id} 更新已經存在的item To-do item
DELETE /api/todo/{id} 刪除指定的item

下面的圖表展示了應用程序的基本設計:

  • 不管是哪個調用 API 的客戶端(瀏覽器、移動應用等)。我們不會在本教程編寫客戶端。
  • model 是一個代表你應用程序數據的類。在本案例中,只有一個模型 to-do 項。模型表現為簡單 C# 類型 (POCOs),
  • controller 是一個處理 HTTP 請求並返回 HTTP 響應的對象。這個示例程序將只會有一個 controller。
  • 為了保證教程簡單我們不使用數據庫。作為替代,我們會把 to-do 項存入內存。但是我們依然包含了一個數據訪問層(不重要的),用來隔離 Web API和數據層。如果想使用數據庫,參考用 Visual Studio 創建 ASP.NET Core MVC 應用程序

安裝 Fiddler

我們不創建客戶端,我們使用 Fiddler 來測試 API。Fiddler 是一個 Web 調試工具可以讓您撰寫的 HTTP 請求進行發送並查看原始的 HTTP 響應。


創建項目

啟動 Visual Studio。從 File 菜單, 選擇 New > Project

選擇 ASP.NET Core Web Application 項目模版。項目命名為 TodoApi 並且點擊 OK

New ASP.NET Core Web Application (.NET Core) - TodoApi 對話框中,選擇 Web API 模版。點擊 OK


添加模型類

模型表示應用程序中的數據對象。在本示例中,唯一使用到的模型是一個 to-do 項。

添加一個名為 "Models" 的目錄。在解決方案瀏覽器中, 右擊項目。選擇 Add > New Folder。把目錄名命名為 Models

注意
你可以把模型類放到項目的任何地方,但是 Models 是約定的默認目錄。

下一步,添加一個 TodoItem 類。右擊 Models 目錄並選擇 Add > New Item

Add New Item 對話框中,選擇 Class 模版。命名類為 TodoItem 並點擊 OK

將生成代碼替換為:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public string Key { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

添加倉儲類

repository 類是一個封裝了數據層的類,包含了獲取數據並映射到實體模型類的業務邏輯。 盡管本例中不使用數據庫,但依舊值得去思考 Repository 是如何注入到我們的 Controller 的。在 Models 目錄下創建 repository 代碼。

定義一個名為 ITodoRepository 的 repository 接口. 通過類模版 (Add New Item > Class)。

using System.Collections.Generic;

namespace TodoApi.Models
{
    public interface ITodoRepository
    {
        void Add(TodoItem item);
        IEnumerable<TodoItem> GetAll();
        TodoItem Find(string key);
        TodoItem Remove(string key);
        void Update(TodoItem item);
    }
}

接口定義了基本的 CRUD 操作。

下一步,添加一個實現 ITodoRepository 接口的 TodoRepository 類:

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;

namespace TodoApi.Models
{
    public class TodoRepository : ITodoRepository
    {
        static ConcurrentDictionary<string, TodoItem> _todos = 
              new ConcurrentDictionary<string, TodoItem>();

        public TodoRepository()
        {
            Add(new TodoItem { Name = "Item1" });
        }

        public IEnumerable<TodoItem> GetAll()
        {
            return _todos.Values;
        }

        public void Add(TodoItem item)
        {
            item.Key = Guid.NewGuid().ToString();
            _todos[item.Key] = item;
        }

        public TodoItem Find(string key)
        {
            TodoItem item;
            _todos.TryGetValue(key, out item);
            return item;
        }

        public TodoItem Remove(string key)
        {
            TodoItem item;
            _todos.TryGetValue(key, out item);
            _todos.TryRemove(key, out item);
            return item;
        }

        public void Update(TodoItem item)
        {
            _todos[item.Key] = item;
        }
    }
}

生成應用程序確保沒有任何編譯錯誤。


注冊倉儲

定義 repository 接口, 我們可以從使用它的 MVC Controller 解耦倉儲類,而不是直接在 Controller 里面實例化 TodoRepository ,我們將會用 ASP.NET Core 內置功能注入 ITodoRepository ,更多請參考依賴注入

這種方式可以更容易地對你的 Controller 進行單元測試。單元測試應該注入一個 Mock 或 stub 的 ITodoRepository。通過這樣的方式測試范圍可以限制在業務邏輯層而非數據訪問層。

為了注入 repository 到 controller,我們必須注冊DI容器。打開 Startup.cs 文件。添加以下指令:

using TodoApi.Models;

ConfigureServices 方法中,添加高亮方法:

public void ConfigureServices(IServiceCollection services)
{
  // Add framework services.
  services.AddMvc();
  // Add our repository type,下行高亮
  services.AddSingleton<ITodoRepository, TodoRepository>();
}

添加控制器

在解決方案瀏覽器中,右擊 Controllers 目錄。選擇 Add > New Item。在 Add New Item 對話框中,選擇 Web API Controller Class 模版。命名為 TodoController

將生成的代碼替換為如下代碼:

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using TodoApi.Models;

namespace TodoApi.Controllers
{
  [Route("api/[controller]")]
  public class TodoController : Controller
  {
    public TodoController(ITodoRepository todoItems)
    {
      TodoItems = todoItems;
    }
    public ITodoRepository TodoItems { get; set; }
  }
}

這里定義了一個空的 controller 類。下一個章節,我們將添加代碼來實現 API。


獲取 to-do 列表

為了獲取 to-do 項,添加下列方法到 TodoController 類。

public IEnumerable<TodoItem> GetAll()
{
    return TodoItems.GetAll();
}

[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)
{
  var item = TodoItems.Find(id);
  if (item == null)
  {
    return NotFound();
  }
  return new ObjectResult(item);
}

以下是 GetAll 方法 HTTP 響應:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/10.0
Date: Thu, 18 Jun 2015 20:51:10 GMT
Content-Length: 82

[{"Key":"4f67d7c5-a2a9-4aae-b030-16003dd829ae","Name":"Item1","IsComplete":false}]

在后面的教程中,我將會告訴你如何使用 Fiddler 工具查看 HTTP 響應。

路由和 URL 路徑

[HttpGet] 特性標明這些方法支持 HTTP GET,對 controller 中的方法標注 url 路徑,有以下步驟:

  • 在 controller 特性中標注模板:[Route("api/[controller]")]
  • 替換模板中的 [Controller] 為實際 controller 名稱(在這里和以前一樣,約定優於配置,所有 controller 都必須以 Controller 結尾,模板中則可以省略 Controller 后綴,譯者注)。比如本例中的 TodoController,把 {controller} 換成 todo 即可。在 ASP.NET MVC Core 是大小寫不敏感的。
  • 如果 [HttpGet] 也有一個模板,可以把它附加到路徑后面,本例中不使用模板字符串。

對於 GetById 方法,在實際 HTTP 請求中 {id} 是一個占位符,客戶端運行時使用 todo 的 ID 屬性;當 MVC 調用 GetById 時,會把 {id} 占位符分配到 Url 方法的 id 參數中。

更換 "api/todo" 的啟動 Url

  • 右擊項目 > Properties
  • 選擇 Debug 選項卡,將 Launch URL 的值設置為 api/todo

了解更多有關請求路由的信息請參考路由到控制器 Action

返回值

GetAll 方法返回 CLR 對象。MVC 自動把對象序列化為 JSON 並把 JSON 對象寫入響應消息正文。響應狀態碼為 200,假設沒有未處理異常的情況下。(未處理異常一般會被轉化為 5xx 錯誤。)

相反,GetById 將會返回 IActionResult 類型,它代表了更加通用的結果對象。因為 GetById 有兩個不同的返回值:

  • 如果沒有數據項可以匹配 ID,方法會返回 404 錯誤,並最終以返回 NotFound 告終。
  • 否則方法會返回 200 以及 JSON 響應正文。並最終以返回 ObjectResult 告終。

使用 Fiddler 調用 API

這一步是可選的,但是有助於我們查看 Web API 返回的原始 HTTP 響應。

在 Visual Studio 中,點擊 ^F5 啟動項目。Visual Studio 啟動瀏覽器並導航到 http://localhost:port/api/todoport 是一個隨機數。如果你使用 Chrome、Edge 或者 Firefox 瀏覽器,todo 數據將會被顯示。如果你使用 IE,IE 將會彈出窗口提示要求打開或者保存 todo.json 文件。

啟動 Fiddler,從 File 菜單,取消選擇 Capture Traffic 選項。這個會關閉捕獲 HTTP traffic。

選擇 Composer 頁面。在 Parsed 選項卡中,輸入 http://localhost:port/api/todoport 是實際的端口號。點擊 Execute 發送請求。

結果會顯示在 sessions 列表中,響應碼是200。使用 Inspectors 選項卡來查看響應內容,包括請求正文。


實現其他的 CRUD 操作

最后一步是為 Controller 增加 CreateUpdate 以及 Delete 這三個方法。這些方法都是圍繞着一個主題,所以我將只列出代碼以及標注出主要的區別。

Create

[HttpPost]
public IActionResult Create([FromBody] TodoItem item)
{
  if (item == null)
  {
    return BadRequest();
  }
  TodoItems.Add(item);
  return CreatedAtRoute("GetTodo", new { controller = "Todo", id = item.Key }, item);
}

這是一個 HTTP POST 方法,用 [HttpPost] 特性聲明。[FromBody] 特性告訴 MVC 從 HTTP 請求的正文中獲取 to-do 項的值。

當通過客戶端向服務器發出 POST 請求來創建新資源時,CreatedAtRoute 方法將返回標准的 201 響應。CreateAtRoute 還把 Location 頭信息加入到了響應。Location 頭信息指定新創建的 todo 項的 URI。查看 10.2.2 201 Created

我們使用 Fiddler 來創建和發送一個請求:

  1. Composer 頁面,從下拉框選擇 POST。
  2. 在請求頭的文本框中, 添加 Content-Type: application/json,意思是 Content-Type 類型的頭信息值為 application/json。Fiddler 會自動添加 Content-Length 頭信息。
  3. 在請求正文的文本框,輸入以下內容:{"Name":"<你的 to-do 項目>"}
  4. 點擊 Execute

這是一個簡單的 HTTP 會話. 使用 Raw 選項卡查看會話數據.

Request:

POST http://localhost:29359/api/todo HTTP/1.1
User-Agent: Fiddler
Host: localhost:29359
Content-Type: application/json
Content-Length: 33

{"Name":"Alphabetize paperclips"}

Response:

HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: http://localhost:29359/api/Todo/8fa2154d-f862-41f8-a5e5-a9a3faba0233
Server: Microsoft-IIS/10.0
Date: Thu, 18 Jun 2015 20:51:55 GMT
Content-Length: 97

{"Key":"8fa2154d-f862-41f8-a5e5-a9a3faba0233","Name":"Alphabetize paperclips","IsComplete":false}

Update

[HttpPut("{id}")]
public IActionResult Update(string id, [FromBody] TodoItem item)
{
  if (item == null || item.Key != id)
  {
    return BadRequest();
  }

  var todo = TodoItems.Find(id);
  if (todo == null)
  {
    return NotFound();
  }

  TodoItems.Update(item);
  return new NoContentResult();
}

Update 類似於 Create,但是使用 HTTP PUT。響應是 204 (No Content)。根據 HTTP 規范,PUT 請求要求客戶端發送整個實體更新,而不僅僅是增量。為了支持局部更新,請使用 HTTP PATCH。

Delete

[HttpDelete("{id}")]
public void Delete(string id)
{
  TodoItems.Remove(id);
}

方法返回 204 (無內容) 響應。這意味着客戶端會收到 204 響應即使該項目已被刪除,或者根本不存在。有兩種方法來處理請求刪除不存在資源的問題:

  • "Delete" 代表「刪除一個已存在的項」,如果不存在返回 404。
  • "Delete" 代表「確保該項不在集合中」,如果項目不在集合中返回 204。

無論哪種方法是合理的。如果收到 404 錯誤,客戶端將需要處理這種情況。


下一步

返回目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM