差異處
這裏顯示兩個版本的差異處。
Both sides previous revision 前次修改 下次修改 | 前次修改 | ||
java:web:restapi:http_method_cant_make_sense [2017/09/26 22:06] tony [Summary] |
java:web:restapi:http_method_cant_make_sense [2023/06/25 09:48] (目前版本) |
||
---|---|---|---|
行 1: | 行 1: | ||
{{tag>rest}} | {{tag>rest}} | ||
- | ====== Http Method無法表達出某些動作(施工中) ====== | + | ====== Http Method無法表達出某些動作 ====== |
===== Introduction ===== | ===== Introduction ===== | ||
在設計RestAPI時,並非所有domain操作都符合CRUD;例如,model中資料的同步(sync)、有transaction的行如轉換(transfer)、搬移(move)、複製(copy)等;因此做些案例研究看是否能找尋到自己能滿意的做法。本篇文章是根據研究結果,分享針對操作(action)做resource modeling的心得。 | 在設計RestAPI時,並非所有domain操作都符合CRUD;例如,model中資料的同步(sync)、有transaction的行如轉換(transfer)、搬移(move)、複製(copy)等;因此做些案例研究看是否能找尋到自己能滿意的做法。本篇文章是根據研究結果,分享針對操作(action)做resource modeling的心得。 | ||
行 14: | 行 14: | ||
PATCH /Systems/1/BIOS/Settings | PATCH /Systems/1/BIOS/Settings | ||
</code> | </code> | ||
- | 接著是更新firmware,以[[http://www.dell.com/support/manuals/tw/en/twdhs1/dell-active-system-mngr-v8.1/asm%20rest%20api-v1/manageddevicefirmware-put?guid=guid-ad63b2c1-00c4-4ce3-ad7a-a498e15f6636&lang=en-us|Dell ASM REST API]]為例,使用store resource的方式去操作: | + | 接著是更新firmware,以[[https://cpsdocs.dellemc.com/bundle/PFMGR_API_3/page/GUID-87FE88BA-FE97-4ECA-BF1E-46A23CC6414F.html|Dell ASM REST API]]為例,使用store resource的方式去操作: |
<code> | <code> | ||
PUT /ManagedDevice/firmware | PUT /ManagedDevice/firmware | ||
行 95: | 行 95: | ||
==== The procedural action ==== | ==== The procedural action ==== | ||
- | 這個能列舉的範例很多,只要不是CRUD的操作,你可以選擇把它model唯一個controller resource: | + | 這個能列舉的範例很多,只要不是CRUD的操作,你可以選擇把它model為controller resource: |
<code> | <code> | ||
Search | Search | ||
行 118: | 行 118: | ||
- hypermedia: 我們有辦法表達出取得狀態、關機、開機等的link嗎? | - hypermedia: 我們有辦法表達出取得狀態、關機、開機等的link嗎? | ||
- synchronized: 在我們做開機與關機動作後,是可以立即反應的嗎? | - synchronized: 在我們做開機與關機動作後,是可以立即反應的嗎? | ||
- | - implementation: 這部分稍後再做說明。 | + | - implementation: 如果使用PATCH的話,我比較不是那麼喜愛,原因稍後再做說明。 |
- | 或許把它做成store resource會比較好: | + | 考慮以上原因,或許把它做成store resource會比較好: |
<code> | <code> | ||
GET /hosts/123/power_status | GET /hosts/123/power_status | ||
行 128: | 行 128: | ||
這裡我並沒有使用DELETE,因為在語義上使用DELETE power_on不會比PUT power_off來得清楚。此外,在使用這種方式後,hypermedia可以很容易的使用URI去表達出不同的意義。剩下的問題就是synchronized。\\ | 這裡我並沒有使用DELETE,因為在語義上使用DELETE power_on不會比PUT power_off來得清楚。此外,在使用這種方式後,hypermedia可以很容易的使用URI去表達出不同的意義。剩下的問題就是synchronized。\\ | ||
\\ | \\ | ||
- | PUT回傳202的方式,我自己本身還沒看過範例,但HTTP規格書也沒說這樣是不對的。我覺得值得討論的部分是: Idempotent。假設PUT回傳202,這代表著server將產生一個asynchronized的task,每次PUT power_on所產生的task是否會相同呢? 假如不同,是不是代表違反了Idempotent? 假如相同,實作會不會蠻奇怪的呢?\\ | + | PUT回傳202的方式,目前有看過[[https://techlibrary.hpe.com/docs/enterprise/servers/oneview5.0/cicf-api/en/index.html#rest/enclosures|HPE OneView REST API]]存在這樣的設計;HTTP規格書也沒說這樣是不對的。我覺得值得討論的部分是: idempotent。假設PUT回傳202,這代表著server將產生一個asynchronized的task,每次PUT power_on所產生的task是否會相同呢? 假如不同,是不是代表違反了idempotent? 假如相同,實作會不會蠻奇怪的呢? 這個我目前沒有答案。\\ |
\\ | \\ | ||
另外一個選擇,就是把它當controller resource,使用POST去操作: | 另外一個選擇,就是把它當controller resource,使用POST去操作: | ||
行 135: | 行 135: | ||
POST /hosts/123/power_off | POST /hosts/123/power_off | ||
</code> | </code> | ||
- | 有個實際案例就是[[https://pubs.vmware.com/vcd-51/index.jsp?topic=/com.vmware.vcloud.api.reference.doc_51/doc/operations/POST-PowerOffVApp.html|vCloud]]的Power On/Off API。順便提一下,會使用這個範例是由於在Roy Fielding在2008年[[http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post|It is okay to use POST]]文章中,留言區有人提及;而Roy Fielding也是傾向於將狀態與操作model為不同狀態,這讓我很好奇為什麼他會這樣想。 | + | 有個實際案例就是[[https://pubs.vmware.com/vcd-51/index.jsp?topic=/com.vmware.vcloud.api.reference.doc_51/doc/operations/POST-PowerOffVApp.html|vCloud]]的Power On/Off API。順便提一下,會使用這個範例是由於在Roy Fielding在2008年[[http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post|It is okay to use POST]]文章中,留言區有人提及此範例;而Roy Fielding也是傾向於將狀態與操作model為不同狀態,這使我思考為什麼他會這樣想。 |
===== Some questions ===== | ===== Some questions ===== | ||
- | 針對以上提到的方法,我思考著幾個問題: | + | 針對以上提到的方法,我還思考著幾個問題: |
==== Why don't you use the query string or formdata to pass the action? ==== | ==== Why don't you use the query string or formdata to pass the action? ==== | ||
(這裡先撇開query string、formdata或request body等方式的差異) 對我而言,我目前認為有幾點需要考量: | (這裡先撇開query string、formdata或request body等方式的差異) 對我而言,我目前認為有幾點需要考量: | ||
- hypermedia: 假如要表達出query string等方式,會需要使用template的方式,實作上不會比單純透過URI Path的方式容易。 | - hypermedia: 假如要表達出query string等方式,會需要使用template的方式,實作上不會比單純透過URI Path的方式容易。 | ||
- http method convention: 如果在collection上使用POST,是否會讓新增與其它action讓使用者混淆。 | - http method convention: 如果在collection上使用POST,是否會讓新增與其它action讓使用者混淆。 | ||
- | - implementation: | + | - implementation: 我們看看如果使用URI的Path來實做lock與unlock可能會長怎樣: |
- | 我們看看如果使用URI的Path來實做lock與unlock可能會長怎樣: | + | |
<code java> | <code java> | ||
@RequestMapping(value = "/files/{fid}/lock", method = RequestMethod.PUT, produces = {"application/json"}) | @RequestMapping(value = "/files/{fid}/lock", method = RequestMethod.PUT, produces = {"application/json"}) | ||
行 170: | 行 169: | ||
} | } | ||
</code> | </code> | ||
- | 你喜歡哪個? 假如File的action只有lock與unlock,那真的是天下太平;但事實上,action還有move、copy、rename等。考慮一下測試、維護、擴充的話,哪一個會比較好? 以擴充與維護來說,RequestParam的方式讓所有的action都必須接受同一組參數甚至輸出,增加了修改的麻煩;而URI Path則由各別的實做去決定。測試則是因為實做已分開,根據個別操作的目的去測試即可。 | + | 你喜歡哪個? 假如File的action只有lock與unlock,那真的是天下太平;但事實上,action還有move、copy、rename等。考慮一下測試、維護、擴充的話,哪一個會比較好? 以擴充與維護來說,RequestParam的方式讓所有的action都必須接受同一組參數甚至輸出,增加了修改的麻煩;URI Path則由各別的實做去決定。測試則是因為實做已分開,根據各別操作的目的去測試即可。 |
==== Threat the P/N action as a store resource or use PATCH? ==== | ==== Threat the P/N action as a store resource or use PATCH? ==== | ||
在我們的專案中,P/N action我傾向使用store resource大於PATCH。主要原因有以下: | 在我們的專案中,P/N action我傾向使用store resource大於PATCH。主要原因有以下: | ||
行 187: | 行 186: | ||
file.locked = Boolean.parseBoolean(updateFile.locked); | file.locked = Boolean.parseBoolean(updateFile.locked); | ||
if( updateFile.name != null ) | if( updateFile.name != null ) | ||
- | file.name = updateFile.name; | + | file.name = updateFile.name; |
return new ResponseEntity<String>("good", HttpStatus.OK); | return new ResponseEntity<String>("good", HttpStatus.OK); | ||
} | } | ||
行 194: | 行 193: | ||
這樣抉擇的發生,是建立在動作屬於Resource中的一個attribute時。 | 這樣抉擇的發生,是建立在動作屬於Resource中的一個attribute時。 | ||
===== Summary ===== | ===== Summary ===== | ||
- | 面對一個操作,我們該如何model為resource呢? | + | 面對一個非CRUD的domain操作,我們該如何model為resource呢? |
- | - 盡力把它變成service resource,參考他人做法。 | + | - 盡力把它變成service resource,可參考他人做法。 |
- | - | + | - 確認是否為一個procedure,是的話就把它當controller resource;可以把非同步的性質當前置條件。 |
- | + | - 確認是否為resource中的attribute,是的話可以使用PATCH。 | |
- | + | - 如果不想用PATCH,可以考慮作為store resource。 | |
- | [[.:http_method_cant_make_sense:other_case_studies|其它的案例]] | + | 以上方法可以當參考,還是要以需求為重;另外補充我看過的[[.:http_method_cant_make_sense:other_case_studies|其它的案例]]。 |
===== Reference ===== | ===== Reference ===== | ||
* [[https://stackoverflow.com/questions/2173721/why-does-including-an-action-verb-in-the-uri-in-a-rest-implementation-violate-th|Why does including an action verb in the URI in a REST implementation violate the protocol?]] | * [[https://stackoverflow.com/questions/2173721/why-does-including-an-action-verb-in-the-uri-in-a-rest-implementation-violate-th|Why does including an action verb in the URI in a REST implementation violate the protocol?]] |