RESTful Web Service 初探

昨天使用了 Scaffolding 自動產生 Web API 控制器的動作方法,可以對地址(Address)物件做新增、修改、刪除,以及查詢的動作,今天就來一面深入查看這些工具自動產生的程式碼的內容,並實際操作體驗和測試這些,新增、修改、刪除,查詢的功能是否可以正常運作。

Postman 測試 Web API 的利器

雖然目前寫好的 Web API 已有與住址相關資料的 CRUD (Create、Read、Update、Delete)功能可供操作測試,但是前端的使用者介面還沒有實作完成,而有必要找尋一個可消費 Web API 的測試工具,所以在開始測試之前,先來求問一下 Google 有沒有好用的 Web API 測試工具,看來 Postman 是不錯用的工具,就先下載來安裝使用吧:

https://www.getpostman.com/

安裝完 Postman 後,執行畫面如下圖所示,請留意圖中以橢圓圈起來的地方,這會配合接下來的程式解說時使用:

RESTful Web Service

阿源哥哥不想在此花時間解釋什麼是 RESTful Web Service 因為隨便 Google 一下就可以找到一篇比阿源哥哥寫的好的文章,但是不稍微說明一下的話,讀者可能會不太明白接下來要學習的操作內涵,所以就使用一張阿源哥哥上課時所使用的投影片來說明:

首先請回憶一下,先前說過整個應用程式的操作分為《前端》和《後端》兩部分。這時候的《前端》可以想像是我們接下來要開發的行動裝置應用程式(今天暫時用 Postman 代替),而《後端》也就是昨天我們實作出來的可對資料庫進行 CRUD 操作的 Web API 控制器動作方法(Controller Action Method)。

理解了應用程式有《前端》和《後端》之分之後,接下來的問題就是《前端》和《後端》如何溝通,也就是《前端》如何告訴《後端》想請它做什麼事,因此《前端》必需攜帶足夠的訊息到《後端》,然後《後端》才會知道《前端》要叫它做什麼事,接著執行所要求的工作,並回傳執行結果。因此前端必需至少要帶三個訊息:

  1. 名詞:講白一點就是他想請誰工作,而這個誰一般是使用類似網頁網址的 URL ,該被填在前面的 Postman 介面的「Enter request URL」的部分。
  2. 動詞:講白一點是要做什麼事,而要做的事不外乎是 GET、POST、PUT、DELETE 這些資料庫的操作,該在前面的 Postman 介面選擇。
  3. 格式:設定兩者資料傳遞的格式,一般內定是使用 JSON

路由(Route)設定

剛剛在前面說過,要找到對的人請他工作,所以在控制器類別上加個 [Route("api/Addresses")]  修飾詞,將來使用 http://yourdomain/api/Addresses  即可對應到此控制器:

namespace Demae.Api.Controllers
{
    [Produces("application/json")]
    [Route("api/Addresses")]
    public class AddressesController : Controller
    {
        private readonly DemaeContext _context;

        public AddressesController(DemaeContext context)
        {
            _context = context;
        }

        ......
        ......
        ......



    }
}

而上述的 yourdomain 是什麼呢?請看下面的操作圖示,在測試階段為 localhost:18098 (註:讀者所看到的埠號可能是不同的編號,請在測試時使用自己的編號),所以在 Postman 操作介面上要填入 http://localhost:18098/api/Addresses 才可對應到(找到對的人)實作的地址相關控制器:

名詞知道了,啊!動詞呢?由如下程式碼所示,加入 [HttpGet] 修飾詞的動作方法,即可讓 Postman 下達 GET 動詞,如果沒帶 id 參數就是要執行列表(列出資料庫中所有地址),而有帶 id 參數的,就是要查詢主鍵值為所帶入 id 數值的地址:

// GET: api/Addresses
[HttpGet]
public IEnumerable<Address> GetAddresses()
{
    return _context.Addresses;
}

// GET: api/Addresses/5
[HttpGet("{id}")]
public async Task<IActionResult> GetAddress([FromRoute] int id)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    var address = await _context.Addresses.SingleOrDefaultAsync(m => m.Id == id);

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

    return Ok(address);
}

同理,加入 [HttpPut("{id}")]  修飾詞,即可讓 Postman 下達 PUT 動詞來執行這沒修改的程式:

[HttpPut("{id}")]
public async Task<IActionResult> PutAddress([FromRoute] int id, [FromBody] Address address)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    if (id != address.Id)
    {
        return BadRequest();
    }

    _context.Entry(address).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!AddressExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

也是相同道理,加入 [HttpPost] 修飾詞,即可讓 Postman 下達 POST 動詞來執行這沒新增的程式:

[HttpPost]
public async Task<IActionResult> PostAddress([FromBody] Address address)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    _context.Addresses.Add(address);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetAddress", new { id = address.Id }, address);
}

最後的是,加入 [HttpDelete] 修飾詞,即可讓 Postman 下達 DELETE 動詞來執行這沒刪除的程式:

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteAddress([FromRoute] int id)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    var address = await _context.Addresses.SingleOrDefaultAsync(m => m.Id == id);
    if (address == null)
    {
        return NotFound();
    }

    _context.Addresses.Remove(address);
    await _context.SaveChangesAsync();

    return Ok(address);
}

好吧!時間不多了,今天就到些此為止,原先預定的要實際使用 Postman 進行 CRUD 的操作,以及詳細解說內部的程式碼,就留待下星期一再來努力了。