iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
Modern Web

網站一條龍 - 從架站到前端系列 第 6

[Day06] 用 .NET 實做簡單的 RESTful API

HTTP request
上次我們建立了一個直接能跑的專案,但是它只有一個回傳隨機天氣的 API 接口:WeatherForecastController 的 Get() Action。當我們執行專案,我們在 Swagger 頁面可以看到這個接口的資訊GET API

當我們點擊這個 API 然後再點 Try it out -> Execute,這個頁面就會發出一個 HTTP request 到我們正在執行的 API server。在網站的應用中,HTTP 是最常用來互相溝通的通訊協定,最常用的 request 有

  • GET - 通常用來讀取資料
  • POST - 通常用來根據使用者輸入的表單新增資料
  • PUT - 通常用來更新資料
  • DELETE - 通常用來刪除資料

在這裡筆者加上「通常」的原因是,其實我們接到請求之後想要在 controller action 裡面做什麼都可以。例如筆者第一份工作時我的 API 只有分 GET 跟 POST,除了讀資料以外通通用 POST 處理。當然,這是很不好的示範,看完這篇文章以後,請盡量不要這麼幹 XD

HTTP 是一個非常大的主題,想了解細節的邦友可以參考這篇文章,圖文並茂,寫得詳細又好懂。

RESTful API
RESTful API 是一種 API 設計風格,這幾年來越來越流行,甚至可以說已經成為主流,因為他直覺又好管理。以前筆者那種「除了 GET 其他全部 POST」的做法,到後面 Controller 裡會有一大堆需要讀名字才能知道它在做什麼的 action,例如 CreateUser(), UpdateUser, DeleteUser()。網站一大,API 就開始越來越難管理,只好前端跟後端共享一個 API 文件來彼此溝通。

使用 RESTful API 以後,目標主體(User)搭配 HTTP 動詞(GET, POST, PUT, DELETE)就能做到我們需要的功能,而 API 接口確變得非常簡潔、優雅

  • [HttpGet] localhost:5000/User 就代表跟 API 索取使用者資料
  • [HttpPost] localhost:5000/User 就代表叫 API 新增一個使用者
  • [HttpPut] localhost:5000/User/1 就代表叫 API 更新 ID 為 1 的使用者
  • [HttpDelete] localhost:5000/User/2 叫 API 刪除 ID 為 2 的使用者

RESTful API 還有許多優點,也有更多詳細的規範,想要了解更多,請參考這篇文章,一樣圖文並茂,寫得詳細又好懂。

實作 RESTful API
現在,我們要來實作一個陽春的 RESTful API。首先,我們先來新增負責 API 接口的 Controller,對 Controllers 點右鍵 -> 新增控制器 -> 選擇通用分類下的 API -> 選擇執行讀取/寫入的 API 控制器。然後!Tada~!一個 RESTful 的 Controller 就出現了!Visual Studio 就是這麼香。
https://ithelp.ithome.com.tw/upload/images/20210906/20140664NyA241r4iU.png

接著,新增用來代表使用者的 class。筆者習慣在專案底下新增一個 Models 資料夾,再把代表資料的 class 放在這邊:對Models 資料夾點右鍵 -> 新增類別 -> 寫上一些簡單的屬性
https://ithelp.ithome.com.tw/upload/images/20210906/20140664mMeKtijmxa.png

最後,把回傳的資料型態改一改,再把 CRUD (Create, Read, Update, Delete)的程式碼加到對應的 Controller Action 就完成了。

[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
    private static List<User> _users = new List<User>()
    {
        new User() {UserId = 0, UserName = "Alice", Email="alice@test.mail"},
        new User() {UserId = 1, UserName = "Bob", Email="bob@test.mail"},
        new User() {UserId = 2, UserName = "Cathy", Email="cathy@test.mail"},
    };
    
    // GET: api/<UserController>
    [HttpGet]
    public IEnumerable<User> Get()
    {
        return _users;
    }

    // GET api/<UserController>/5
    [HttpGet("{id}")]
    public User Get(int id)
    {
        var user = _users.FirstOrDefault(x => x.UserId == id);
        if (user == null)
        {
            throw new Exception("找不到 user");
        }
        return user;
    }

    // POST api/<UserController>
    [HttpPost]
    public void Post(User user)
    {
        user.UserId = _users.Max(x => x.UserId) + 1;
        _users.Add(user);
    }

    // PUT api/<UserController>/5
    [HttpPut("{id}")]
    public void Put(int id, User newUserData)
    {
        var existingUser = _users.FirstOrDefault(x => x.UserId == id);
        if (existingUser == null)
        {
            throw new Exception("找不到 user");
        }
        existingUser.UserName = newUserData.UserName;
        existingUser.Email = newUserData.Email;
    }

    // DELETE api/<UserController>/5
    [HttpDelete("{id}")]
    public void Delete(int id)
    {
        var existingUser = _users.FirstOrDefault(x => x.UserId == id);
        if (existingUser == null)
        {
            throw new Exception("找不到 user");
        }
        _users.Remove(existingUser);
    }
}

上面的程式碼有幾個地方要注意:

  1. [Route("api/[controller]")] 指示 .NET 把 Controller 的名字當成路由的一部份,意即要使用這個 Controller 時,URL 要輸入 "網址/api/Controller名字/"
  2. [ApiController] 標記了這個 Controller 是 API Controller,會幫我們套用一些預設設定,例如複雜的型別預設是 [FromBody],也就是說,加了 [ApiController] 這個屬性之後,原本 PUT action 的 [FromBody] 可以拿掉
  3. 因為目前我們還沒有使用資料庫,所以偷懶用了一個 static 變數儲存資料,這是不好的做法!等到我們開始使用資料庫就會把這個換掉
  4. 這裡的資料操作使用 LINQ,本系列文會使用 Dapper 操作資料庫所以不會特別說明,但是 LINQ 真的是方便又好用,如果有心想寫 .NET 誠心建議把它學起來,這裡一樣推薦一篇 LINQ 的延伸閱讀
  5. throw new Exception 也是不好的用法,會讓真正出錯的 Exception 難以被追蹤,我們會在後面的文章改掉

上一篇
[Day05] Web API 專案架構
下一篇
[Day07] Service 與 Dependency Injection (依賴注入)
系列文
網站一條龍 - 從架站到前端33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言