設計web API時, 遵循REST架構的好處很多,這邊就不多提了,這邊我想紀錄一下自己在設計過程中遇到的一些問題與思維。
借用Ruby on Rails實戰聖經裡的例子:
Helper | GET | POST | PATCH/PUT | DELETE |
---|---|---|---|---|
event_path(@event) | /events/1 show action | /events/1 update action | /events/1 destroy action | |
events_path | /events index action | /events create action |
新增一筆事件時,我們用:
POST /events要取得多筆事件時,我們用:
GET /events問題來了,如果我們想要一次新增多筆事件時,該怎麼辦?
首先,必須先思考是否一定要這麼做,是否發多個POST request就能夠滿足需求,如果一定要透過一個request來新增多筆事件,
我們可以怎麼做呢?假設"event"是我們的一種resource,平時新增一筆事件時,body帶的資料如下(以JSON為例):
{ "date": "2015-7-18", "subject": "summer event", "location": "Taipei" }Server會回傳:
{ "id": 1 }
想像我們有另一種resource,叫做"event-set",怎我們可以定義一個API為:
POST /event-set可以帶這樣的body:
[ { "date": "2015-7-18", "subject": "summer event", "location": "Taipei" }, { "date": "2015-9-26", "subject": "fall event", "location": "Kaohsiung" }, { "date": "2015-12-22", "subject": "winter event", "location": "Taichung" } ]Server則需根據原本順序回傳每筆event的id:
[ { "id": "1" }, { "id": "2" }, { "id": "3" } ]則client就能夠針對回傳的每個id,針對每個event操作。寫到這可能有人已經跳腳了,沒錯,我定義了一個叫"event-set"的resource,但卻沒有提供對"event-set"的identification,這樣,還算是RESTful嗎?
這樣做有點破壞了統一介面的特性,破壞了RESTful簡單的特性,也需要考慮client一次發送過多的新增數量(例如一個request要求建立一百萬筆事件)造成的延遲等問題。沒有一種能夠治百病的架構,想清楚自己的目的是什麼,為何如此設計。REST很適合作為web應用的架構風格,而自己一定更清楚自己的需求。
一次要修改多筆event可以這麼做(同樣的,必須想清楚是否真的需要這樣的API):
{ "1": { "data": "2015-7-19" }, "2": { "subject": "autumn event" } }
另一個問題,假設每個event下有多個訂單(order),則我們可以設計以下的API來操作訂單:
POST /events/1/orders GET /events/1/orders GET /events/1/orders/1 PATCH /events/1/orders/2 DELETE /events/1/orders/3
但這樣的設計,如果套用在實際應用上時,發現每筆訂單其實都要分法給不同單位各自處理,而每個單位並不知道也不需要知道該訂單適用在哪個event上,若我們經常需要獨立對每筆訂單做操作,則我們可以改成這樣的API設計:
POST /orders GET /orders/a3e59a2c16ff44c69841db98dcd1a618 GET /orders PATCH /orders/b2310ed23e534568876d1a3dff626690 DELETE /orders/67f1a8af50b8464ba7f8-741b20aba4a不同單位的訂單可以做不同的權限控制,以避免被其他單位存取。同時,改用hash number作為id,可以避免一些存取失誤,例如order 3原來是給event 2的,但不小心拿了order 3給event 3使用。
而我們仍需要知道每筆order是屬於哪個event,因此可以把order id加在每個event的內容中:
GET /events/1
{ "date": "2015-7-18", "subject": "summer event", "location": "Taipei", "orders": [ "a3e59a2c16ff44c69841db98dcd1a618", "b2310ed23e534568876d1a3dff626690", ... ] }但這麼做的話,會導致order資訊永遠都會伴隨著event被提供出來,若想避免此問題,可以再加上query string來決定是否回傳event資訊時同時回傳order資訊
沒有留言 :
張貼留言