昨天有針對使用 Scaffolding 自動產生的地址的 CRUD 動作方法,配合 RESTful 的說明稍微地了解了一下,今天會再進一步對照程式碼,使用 Postman 實際驗證一下執行的結果。
功能對照表
為了方面對照,特別將地址 API 的各個功能整理成下表:
請回想一下昨天談到的 RESTfull Web Service 的說明,在《前端》與《後端》之間的溝通,當《後端》接收到《前端》的請求(Request),知道要做什麼事,在執行完工後之後,還需要回報(Response)給《前端》並告知執行的結果,而這執行結果是以稱為 Status Code 的數字來表示,各個數字的 Status Code 所代表的意義,請參考這一篇的說明。
新增
因為目前資料庫中還沒有地址資料,所以先使用 POST 動作新增一筆地址資料如下:
照理來講 areaId 應該是讓使用者由使用者介面中先選出縣市,再由鄉鎮區中選出對應的 AreaId 才對,但是因為使用者介面還沒有做,所以該 AreaId 就從資料庫的資料表中直接查詢。當填選完適當的資料後在步驟 7 按下【Send】向《後端》送出請求,當後端執行完後回傳新增的地址資料(JSON 格式),以及 201 的 Status Code 表示資料新增成功。
執行並體驗完了新增地址的操作,再來對照一下程式碼,應該更容易明白程式碼的含意:
[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);
}
查詢
已經有新增一筆地址資料,且從回傳的資料中確定新地址的 Id 為 1 了,所以接下來以 GET 動作,在網址以參數 1 進行查詢。(鼓勵讀者以不存在的 Id 試著查詢看看有什麼結果發生):
當按下步驟 3 的【Send】向《後端》送出請求,《後端》接收到該 Id 後,以該 Id 向資料庫查詢是否有該 Id 的地址資料,若有則回傳查詢到的地址資料,以及 200 的 Status Code 表示查詢成功。
執行並體驗完了查詢地址的操作,再來對照一下程式碼,應該更容易明白程式碼的含意:
[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);
}
Eager loading
讀者有沒有發現,前面所查詢的地址資訊,其中 "area": null
表示沒有地區的資料,但是有 "areaId": 29
理應可以對應到 Area 才對,怎麼會是 null 呢?因為,定訂從資料庫中載入資料時不會將相關聯的 Entity 資料一起載入,但是可以使用 Include
方法指示查詢時需要包含哪些關聯資料:
var address = await _context.Addresses.Include(a=>a.Area).SingleOrDefaultAsync(m => m.Id == id);
修改程式加入 .Include(a=>a.Area)
之後,再試著查詢一次,這次 Area 的資料就一起讀進來了,但是 City 呢?
防止序例化循環參考
在包含再上一層 city 資料之前,請在 Demae.Api 專案的 Startup.cs 中加入如下的程式碼,以防止無限循環參考的錯誤發生:
public void ConfigureServices(IServiceCollection services)
{
......
......
......
services.AddMvc().AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
}
再加入 .ThenInclude(c=>c.City) 即可將 City 資料也包含進來:
var address = await _context.Addresses.Include(a=>a.Area).ThenInclude(c=>c.City).SingleOrDefaultAsync(m => m.Id == id);
修改程式加入 .ThenInclude(c=>c.City)
之後,再試著查詢一次,這次 City 的資料就一起讀進來了,但是 City 底下的 Areas 呢?那就繼續 .ThenInclude() 下去啊,留做家庭作業好了。
列表
不帶 Id 參數的 GET 動作為表列出資料庫中所有地址資料,因為目前只有一筆,因此看起來與查詢的結果很相似,但請再留意一下,住址資料外有用 [ ]
括起來,這代表為多筆資料(雖然目前只有一筆)的陣列。
執行並體驗完了表列地址的操作,再來對照一下程式碼,應該更容易明白程式碼的含意:
[HttpGet]
public IEnumerable<Address> GetAddresses()
{
return _context.Addresses;
}
當然讀者也可以試著加入 .Include()
以及 .ThenInclude()
將相關聯的資料載入,也是一樣留做家庭作業,請讀者自己試一下。
刪除
同樣的,要刪除一筆資料,也是給 Id 參數,並使用 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);
}
刪除後再以同一個 Id 查詢,不出所料的傳回的是 404 的沒有這筆資料的 Status Code
好吧!今天就體驗到此,明天再繼續吧。可是 ~~~ 好像修改還沒有體驗齁!因為修改牽涉到並行(Concurrency)處理的問題,改天再一起解釋說明好了。