在Mac下創建ASP.NET Core Web API
這系列文章是參考了.NET Core文檔和源碼,可能有人要問,直接看官方的英文文檔不就可以了嗎,為什么還要寫這些文章呢?
原因如下:
- 官方文檔涉及的內容相當全面,屬於那種大而全的知識倉庫,不太適合初學者,很容易讓人失去重要,讓人掉入到具體的細節之中。
- 對於大多數人來講開發語言只是工具,程序員都有一個通病,就是死磕工具,把工具學深。我認為在工具上沒有必要投入太多時間,以能高效地完成日常的工作項目為准即可。要需求驅動學習,你需要什么學什么。如果你學的新技術新特性只是屠龍之技或者只需要用到的時候去查一下即可的話,這種死磕這又有什么用。沒有必要花120%的時間去學100%的知識,你只需要花20%的時間去學習80%的知識就可以了,剩下的等實際的項目中用到的時候去查就可以了,工具只是工具,不是工作本身。
- 目前基本所有的文章都是基於Windows平台的Visual Studio IDE來介紹的。而我用的是一台Mac,所以我將基於Mac平台的Visual Studio Code講解適合我們實際項目中遇到的知識。
- 還有一點,就是這是我個人的學習總結。
這系列文章就是讓你去花20%的時間去學80%的東西,剩下的20%再去看官方文檔。
在.NET Core里面MVC和WebAPI兩者被整合成一個框架,分享同一套代碼和管線。這樣我們就可以更方便地開發MVC應用程序和Web API接口。
創建項目
在這篇文章中我們將要創建的API如下:
API | 描述 |
---|---|
GET /api/user | 獲取所有的用戶信息 |
GET /api/user/{id} | 根據ID獲取指定的用戶 |
POST /api/user | 添加新的用戶 |
PUT /api/user/{id} | 更新用戶信息 |
PATCH /api/user/{id} | 更新用戶信息 |
DELETE /api/user/{id} | 刪除用戶信息 |
根據上一篇文章,我們通過Yeoman創建一個WebAPI項目,命名為UserWebAPI
:
添加模型類
然后在項目根目錄下面新建一個Models文件夾,在該文件夾下面利用yo aspnet:class UserItem
新建一個UserItem
類。
namespace UserWebAPI.Models
{
public class UserItem
{
public string Key { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
添加倉儲類
Repository類是封裝了數據層的對象,包含了獲取數據、並映射到實體模型類的業務邏輯。
首先我們在Models文件夾下面定義一個IUserRepository
repository接口。
通過運行yo aspnet:interface IUserRepository
命令來創建該接口。
namespace UserWebAPI.Models
{
public interface IUserRepository
{
void Add(UserItem item);
IEnumerable<UserItem> GetAll();
UserItem Find(string key);
UserItem Remove(string key);
void Update(UserItem item);
}
}
接着再添加一個UserRepository
類來實現IUserRepository
接口。
namespace UserWebAPI.Models
{
public class UserRepository : IUserRepository
{
private static ConcurrentDictionary<string, UserItem> _users
= new ConcurrentDictionary<string, UserItem>();
public UserRepository()
{
Add(new UserItem { Name = "Charlie", Age = 18 });
}
public void Add(UserItem item)
{
item.Key = Guid.NewGuid().ToString();
_users[item.Key] = item;
}
public UserItem Find(string key)
{
UserItem user;
_users.TryGetValue(key, out user);
return user;
}
public IEnumerable<UserItem> GetAll()
{
return _users.Values;
}
public UserItem Remove(string key)
{
UserItem user;
_users.TryRemove(key, out user);
return user;
}
public void Update(UserItem item)
{
_users[item.Key] = item;
}
}
}
注冊倉儲
通過定義一個repository
接口,我們從使用它的MVC Controller來解耦該repository
類。我們在此將通過注入一個UserRepository
來代替直接在Controller里面實例化一個UserRepository
類。
為了注入一個repository到controller,我們必須通過DI容器來注冊它,打開Startup.cs文件,在ConfigureServices
方法添加如下代碼:
添加控制器
控制器是用於處理HTTP請求並創建HTTP響應的對象,這里通過運行yo aspnet:webapicontroller UserController
命令生成UserController
控制器。
namespace UserWebAPI.Controllers
{
[Route("api/[controller]")]
public class UserController : Controller
{
public IUserRepository UserItems { get; set; }
public UserController(IUserRepository userItems)
{
UserItems = userItems;
}
}
}
獲取用戶信息
[HttpGet]
public IEnumerable<UserItem> GetAll()
{
return UserItems.GetAll();
}
[HttpGet("{id}", Name = "GetUser")]
public IActionResult GetById(string id)
{
var item = UserItems.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
上述兩個方法實現了兩個GET方法:
GET /api/user
GET /api/user/{id}
運行dotnet restore
、dotnet run
之后,應用程序將會在本機啟動,並在http://localhost:5000
上開啟監聽服務。
然后在Postman上測試你的API接口是否能正確運行。
在GetById
方法中:
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)
其中"{id}"
是UserItem的ID占位符,當GetById
被調用時,URL中的“{id}”
值會被分配給該方法的id
參數。
Name = "GetTodo"
創建了一個命名的路由,並允許你在HTTP響應中鏈接到該路由。
GetAll
方法返回了一個IEnumerable
,MVC會自動將對象序列化成JSON並將JSON寫入到響應消息的正文中。該方法的響應狀態碼為200,假設沒有發生任何未處理異常。
而GetById
方法返回的是一個更為通用的IActionResult
類型。該方法有兩種不同的返回類型:
- 如果沒有項匹配指定的請求ID,該方法通過返回
NotFound
表示一個404錯誤。 - 否則,該方法返回一個JSON響應正文和200響應碼,通過返回
ObjectResult
來表示。
添加新用戶
[HttpPost]
public IActionResult Create([FromBody]UserItem item)
{
if (item == null)
{
return BadRequest();
}
UserItems.Add(item);
return CreatedAtRoute("GetUser", new { id = item.Key }, item);
}
通過[HttpPost]
attribute 標明這個一個HTTP POST方法,[FromBody]
attribute 告訴MVC從HTTP 請求的正文中獲取用戶UserItem值。
CreatedAtRoute
方法返回一個201響應狀態碼(實際上是CreatedAtRouteResult
對象),201
狀態碼是通過POST方法在服務器上成功創建了一個新的資源時的標准響應碼。CreateAtRoute
也在響應里面添加了一個Location頭信息,這個頭信息指定了最新創建的User URI。
/// <summary>
/// Creates a <see cref="CreatedAtRouteResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedAtRouteResult"/> for the response.</returns>
[NonAction]
public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object routeValues,
object value)
{
return new CreatedAtRouteResult(routeName, routeValues, value);
}
/// <summary>
/// Initializes a new instance of the <see cref="CreatedAtRouteResult"/> class with the values
/// provided.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The value to format in the entity body.</param>
public CreatedAtRouteResult(string routeName, object routeValues, object value)
: base(value)
{
RouteName = routeName;
RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues);
StatusCode = StatusCodes.Status201Created;
}
通過查看CreatedAtRouteResult
的構造函數可以看到StatusCode(從ObjectResult對象繼承而來)被直接設置成了Status201Created枚舉值。
201
狀態碼是當你在用POST/PUT在服務器端成功創建了一個新的資源時,服務器就應當返回201 Created
同時在響應頭添加一個Location來指定剛剛創建好的資源的URI。
通過Postman來發送Create請求
剛服務器接收到請求,會在VS Code的控制台顯示出相應的信息:
點擊Headers
tab可以看到Location的值顯示剛剛創建好的資源的URI。
更新用戶信息(HTTP PUT)
[HttpPut("{id}")]
public IActionResult Update(string id, [FromBody] UserItem item)
{
if (item == null || item.Key != id)
{
return BadRequest();
}
var user=UserItems.Find(id);
if(user==null)
{
return NotFound();
}
UserItems.Update(item);
return new NoContentResult();
}
采用了HTTP PUT標記Update
方法,並且響應狀態碼設置為204(No Content)。根據HTTP規范,PUT請求要求客戶端發送整個被更新實體,而不是增量更新部分。如果要支持局部更新,則需要使用HTTP PATCH。
204(No Content)
狀態碼表示服務器已經成功處理了你的請求,但不需要返回具體的數據。瀏覽器不用刷新頁面,也不用重定向到新的頁面,會保留發送了該請求的頁面,不產生任何文檔視圖上的變化,只停留在當前頁面。由於204響應被禁止包含任何消息體,因此它始終以消息頭后的第一個空行結尾。對提交到服務器進行處理的數據,如果只需要返回是否成功的話,可考慮使用狀態碼204來作為返回信息,從而減少多余的數據傳輸。
NoContentResult
類在構造函數中調用了父類的構造函數並把Status204NoContent
傳給了該類。
namespace Microsoft.AspNetCore.Mvc
{
public class NoContentResult : StatusCodeResult
{
public NoContentResult()
: base(StatusCodes.Status204NoContent)
{
}
}
}
更新用戶信息(HTTP PATCH)
[HttpPatch("{id}")]
public IActionResult Update([FromBody] UserItem item, string id)
{
if (item == null)
{
return BadRequest();
}
var user = UserItems.Find(id);
if (user == null)
{
return NotFound();
}
item.Key = user.Key;
UserItems.Update(item);
return new NoContentResult();
}
刪除用戶
[HttpDelete("{id}")]
public IActionResult Delete(string id)
{
var user = UserItems.Find(id);
if (user == null)
{
return NotFound();
}
UserItems.Remove(id);
return new NoContentResult();
}
這個響應狀態碼同樣是204(No Content)
。