เรียกใช้วิธีฝั่งเซิร์ฟเวอร์บนทรัพยากรในวิธีที่สงบ


142

โปรดทราบว่าฉันมีความเข้าใจพื้นฐานเกี่ยวกับส่วนที่เหลือ สมมติว่าฉันมี URL นี้:

http://api.animals.com/v1/dogs/1/

และตอนนี้ฉันต้องการให้เซิร์ฟเวอร์สร้างสุนัขเห่า เฉพาะเซิร์ฟเวอร์เท่านั้นที่รู้วิธีการทำเช่นนี้ สมมติว่าฉันต้องการให้มันทำงานในงาน CRON ที่ทำให้สุนัขเห่าทุก ๆ 10 นาทีตลอดชั่วนิรันดร์ การโทรนั้นมีลักษณะอย่างไร ฉันต้องการทำสิ่งนี้:

คำขอ URL:

ACTION http://api.animals.com/v1/dogs/1/

ในเนื้อหาคำขอ:

{"action":"bark"}

ก่อนที่คุณจะโกรธฉันที่ทำวิธี HTTP ของฉันเองช่วยฉันออกมาและให้แนวคิดที่ดีกว่าเกี่ยวกับวิธีที่ฉันควรเรียกใช้วิธีฝั่งเซิร์ฟเวอร์แบบ RESTful :)

แก้ไขเพื่อความชัดเจน

ชี้แจงเพิ่มเติมเกี่ยวกับสิ่งที่ "เปลือกไม้" วิธีการ นี่คือตัวเลือกบางอย่างที่อาจทำให้การเรียก API ที่มีโครงสร้างแตกต่างกัน:

  1. เรือสำเภาเพียงส่งอีเมลไปที่ dog.email และบันทึกสิ่งใด
  2. bark ส่งอีเมลไปที่ dog.email และการเพิ่มขึ้น dog.barkCount 1
  3. bark สร้างเร็กคอร์ด "bark" ใหม่ที่มีการบันทึก bark.timestamp เมื่อ bark เกิดขึ้น นอกจากนี้ยังเพิ่ม dog.barkCount 1
  4. bark จะรันคำสั่งระบบเพื่อดึงรหัสสุนัขรุ่นล่าสุดลงมาจาก Github จากนั้นจะส่งข้อความไปยัง dog.owner บอกพวกเขาว่ารหัสสุนัขใหม่นั้นกำลังใช้งานอยู่

14
ที่น่าสนใจการเพิ่มความโปรดปรานดูเหมือนจะดึงดูดคำตอบที่แย่กว่าที่คุณเคยได้รับ ;-) เมื่อทำการประเมินคำตอบโปรดจำไว้ว่า: 1) สเปคสำหรับคำกริยา HTTP จะแยกตัวเลือกอื่นนอกเหนือจาก POST 2) REST ไม่มีส่วนเกี่ยวข้องกับโครงสร้าง URL - เป็นรายการทั่วไปของข้อ จำกัด (ไร้สัญชาติ, แคช, เลเยอร์, ​​อินเตอร์เฟสแบบอินเทอร์เฟซและอื่น ๆ ) กว่าประโยชน์ที่มอบให้ (ความยืดหยุ่นความน่าเชื่อถือการมองเห็น ฯลฯ ) 3) การปฏิบัติในปัจจุบัน (เช่นการใช้ POST ในข้อกำหนดของ RPC) ทำให้นักกำหนดนโยบายที่สร้างกฎ API ของตนเองขึ้นมา 4) REST ต้องการอินเตอร์เฟสที่เหมือนกัน (ตามข้อกำหนด HTTP)
เรย์มอนด์ Hettinger

@ โบสถ์คุณคิดอย่างไรกับคำตอบใหม่ ๆ ? มีอะไรที่คุณอยากรู้ แต่ยังไม่ได้กล่าวถึงในข้อใดข้อหนึ่งหรือไม่? ฉันยินดีเป็นอย่างยิ่งที่จะแก้ไขคำตอบของฉันอีกครั้งหากมีประโยชน์มากกว่านี้
Jordan

@RaymondHettinger PATCHสามารถเหมาะสม ผมอธิบายได้ว่าทำไมในช่วงปลายของฉันคำตอบ
Jordan

PATCH จะเหมาะสมสำหรับการเพิ่มdog.barkCountเป็นรายเดียว POST เป็นวิธีการส่งอีเมลสร้างเรกคอร์ด bark ใหม่เรียกใช้คำสั่งเพื่อดาวน์โหลดจาก Github หรือเรียกใช้ข้อความ @Jordan การอ่าน PATCH RFC ของคุณมีความคิดสร้างสรรค์ แต่ค่อนข้างขัดแย้งกับเจตนาที่เป็นตัวแปรของ PUT สำหรับการดัดแปลงทรัพยากรบางส่วน ฉันไม่คิดว่าคุณกำลังช่วย OP ด้วยการหาข้อมูลจำเพาะ HTTP ที่แปลกใหม่แทนที่จะยอมรับวิธีปฏิบัติมาตรฐานของการใช้ POST สำหรับการเรียกขั้นตอนระยะไกล
Raymond Hettinger

@RaymondHettinger ที่มีการปฏิบัติตามพฤตินัยมาตรฐาน POST? อินเทอร์เฟซ RPC มาตรฐานทั้งหมดที่ฉันเคยเห็นระบุทรัพยากรโดยเอนทิตี (ไม่ใช่ RESTful), เทียบกับ URI ดังนั้นคำตอบที่ถูกต้องที่จัดลำดับความสำคัญของการประชุม RPC จะต้องไม่เป็นทางการอยู่ดีซึ่งฉันคิดว่า . คุณไม่เคยผิดพลาดกับ POST เนื่องจากเป็น catch-all สำหรับการประมวลผลข้อมูล แต่มีวิธีการเฉพาะเพิ่มเติม REST หมายถึงการตั้งชื่อทรัพยากรและอธิบายการเปลี่ยนแปลงในสถานะของพวกเขาไม่ใช่การตั้งชื่อขั้นตอนการเปลี่ยนสถานะ PATCH และ POST ทั้งอธิบายการเปลี่ยนแปลงสถานะ
Jordan

คำตอบ:


280

ทำไมต้องมีเป้าหมายในการออกแบบ RESTful

หลักการ RESTful นำคุณสมบัติที่ทำให้เว็บไซต์เป็นเรื่องง่าย (สำหรับผู้ใช้แบบสุ่มเพื่อ "ท่อง") ในการออกแบบเว็บเซอร์วิส APIดังนั้นจึงเป็นเรื่องง่ายสำหรับโปรแกรมเมอร์ที่จะใช้งาน REST ไม่ดีเพราะ REST ดีเพราะดี และมันก็เป็นสิ่งที่ดีมากเพราะมันเป็นเรื่องง่าย

ความเรียบง่ายของธรรมดา HTTP (ไม่มีซองสบู่และเดียวมากเกินไป URI POSTบริการ) สิ่งที่บางคนอาจจะเรียก"ขาดคุณสมบัติ"เป็นจริงพลังที่ยิ่งใหญ่ของมัน HTTP ทันทีที่ขอให้คุณมีความสามารถในการกำหนดแอดเดรสและการไร้สัญชาติ : การตัดสินใจการออกแบบพื้นฐานสองรายการที่ทำให้ HTTP สามารถปรับขนาดได้จนถึงไซต์ขนาดใหญ่ในปัจจุบัน (และบริการเมกะ)

แต่ REST ไม่ใช่ Bulltet เงิน: บางครั้ง RPC-style ("Remote Procedure Call" - เช่น SOAP) อาจเหมาะสมและบางครั้งความต้องการอื่น ๆ ก็มีความสำคัญเหนือกว่าคุณธรรมของเว็บ นี่เป็นเรื่องปกติ สิ่งที่เราไม่ชอบความซับซ้อนจำเป็น บ่อยครั้งที่โปรแกรมเมอร์หรือ บริษัท นำบริการ RPC-style มาใช้กับงานที่ HTTP แบบเก่าธรรมดาสามารถจัดการได้ ผลที่ได้คือ HTTP จะถูกลดขนาดไปที่โปรโตคอลการขนส่งสำหรับส่วนของข้อมูล XML ขนาดใหญ่ที่อธิบายว่าเกิดอะไรขึ้น "จริง ๆ " (ไม่ใช่ URI หรือวิธี HTTP ให้เบาะแสเกี่ยวกับมัน) บริการที่ได้นั้นซับซ้อนเกินไปไม่สามารถทำการดีบักได้และจะไม่ทำงานจนกว่าลูกค้าของคุณจะมีการตั้งค่าที่แน่นอนตามที่นักพัฒนาตั้งใจไว้

ในทำนองเดียวกันโค้ด Java / C # ไม่สามารถเป็นวัตถุได้เพียงแค่ใช้ HTTP ไม่ได้ทำให้การออกแบบสงบ อาจมีใครตกอยู่ในความรีบเร่งในการคิดเกี่ยวกับบริการของพวกเขาในแง่ของการกระทำและวิธีการระยะไกลที่ควรเรียก ไม่น่าแปลกใจที่ส่วนใหญ่จะสิ้นสุดในบริการ RPC-Style (หรือ REST-RPC-hybrid) ขั้นตอนแรกคือการคิดต่างกัน การออกแบบ RESTful สามารถทำได้หลายวิธีวิธีหนึ่งคือคิดถึงแอปพลิเคชันของคุณในแง่ของทรัพยากรไม่ใช่การกระทำ:

💡แทนที่จะคิดในแง่ของการกระทำที่สามารถทำได้ ("ค้นหาสถานที่บนแผนที่") ...

... พยายามคิดในแง่ของผลลัพธ์ของการกระทำเหล่านั้น ("รายการสถานที่บนแผนที่ที่ตรงกับเกณฑ์การค้นหา")

ฉันจะไปตัวอย่างด้านล่าง (แง่มุมสำคัญอื่น ๆ ของ REST คือการใช้ HATEOAS - ฉันไม่ได้แปรงที่นี่ แต่ฉันพูดถึงมันอย่างรวดเร็วที่โพสต์อื่น )


ปัญหาของการออกแบบครั้งแรก

ลองมาดูการออกแบบที่เสนอ:

ACTION http://api.animals.com/v1/dogs/1/

ก่อนอื่นเราไม่ควรพิจารณาสร้างกริยา HTTP ใหม่ ( ACTION) โดยทั่วไปการพูดนี้ไม่พึงประสงค์ด้วยเหตุผลหลายประการ:

  • (1)ด้วย URI ของบริการเท่านั้นโปรแกรมเมอร์ "สุ่ม" จะรู้ACTIONคำกริยาอย่างไร
  • (2)ถ้าโปรแกรมเมอร์รู้ว่ามันมีอยู่เขาจะรู้ว่าความหมายของมันได้อย่างไร? คำกริยานั้นแปลว่าอะไร?
  • (3)ควรมีคุณสมบัติใด (ความปลอดภัยความเฉื่อยชา) ที่คาดหวังว่าคำกริยานั้นมี?
  • (4) จะเป็นอย่างไรถ้าโปรแกรมเมอร์มีไคลเอนต์แบบง่าย ๆ ที่จัดการกริยา HTTP มาตรฐานเท่านั้น
  • (5) ...

ตอนนี้ลองพิจารณาใช้POST (ฉันจะอธิบายว่าทำไมด้านล่างเพียงใช้คำของฉันตอนนี้):

POST /v1/dogs/1/ HTTP/1.1
Host: api.animals.com

{"action":"bark"}

นี่อาจจะโอเค ... แต่ถ้า :

  • {"action":"bark"}เป็นเอกสาร และ
  • /v1/dogs/1/เป็น "ตัวประมวลผลเอกสาร" (เหมือนโรงงาน) URI "ตัวประมวลผลเอกสาร" เป็น URI ที่คุณเพียงแค่ "โยนสิ่งต่าง ๆ ที่" และ "ลืม" เกี่ยวกับพวกเขา - โปรเซสเซอร์อาจเปลี่ยนเส้นทางคุณไปยังทรัพยากรที่สร้างขึ้นใหม่หลังจาก "โยน" เช่น URI สำหรับการโพสต์ข้อความที่บริการนายหน้าข้อความซึ่งหลังจากการโพสต์จะนำคุณไปยัง URI ที่แสดงสถานะของการประมวลผลข้อความ

ฉันไม่รู้เกี่ยวกับระบบของคุณมากนัก แต่ฉันพนันได้เลยว่าไม่เป็นความจริงเลย:

  • {"action":"bark"} ไม่ใช่เอกสารจริงๆแล้วมันเป็นวิธีการที่คุณพยายามจะแอบเข้าไปในบริการ และ
  • /v1/dogs/1/URI หมายถึง "สุนัข" ทรัพยากร (อาจเป็นสุนัขที่มีid==1) และไม่ได้ประมวลผลเอกสาร

ดังนั้นทั้งหมดที่เรารู้ในตอนนี้ก็คือการออกแบบด้านบนนั้นไม่สงบดังนั้น แต่นั่นคืออะไรกันแน่ อะไรที่เลวร้ายเกี่ยวกับมัน? โดยทั่วไปมันไม่ดีเพราะนั่นคือ URI ที่ซับซ้อนพร้อมความหมายที่ซับซ้อน คุณไม่สามารถอนุมานอะไรจากมัน โปรแกรมเมอร์จะรู้ได้อย่างไรว่าสุนัขมีการbarkกระทำที่สามารถแทรกซึมPOSTเข้าไปในที่ลับได้?


การออกแบบการโทร API ของคำถามของคุณ

ดังนั้นขอให้ตัดเพื่อการไล่ล่าและพยายามที่จะออกแบบเปลือกผู้เอ้โดยการคิดในแง่ของทรัพยากร ให้ฉันอ้างอิงหนังสือRestful Web Services :

การPOSTร้องขอคือความพยายามในการสร้างทรัพยากรใหม่จากที่มีอยู่ ทรัพยากรที่มีอยู่อาจเป็นพาเรนต์ของแหล่งข้อมูลใหม่ในความหมายโครงสร้างข้อมูลวิธีที่รูทของต้นไม้เป็นพาเรนต์ของโหนดโหนดทั้งหมด หรือทรัพยากรที่มีอยู่อาจเป็นทรัพยากร"โรงงาน"พิเศษ ซึ่งมีวัตถุประสงค์เพียงอย่างเดียวคือการสร้างทรัพยากรอื่น ๆ การเป็นตัวแทนที่ส่งไปพร้อมกับPOSTคำขอจะอธิบายถึงสถานะเริ่มต้นของทรัพยากรใหม่ เช่นเดียวกับ PUT POSTคำขอไม่จำเป็นต้องรวมการเป็นตัวแทนเลย

จากคำอธิบายข้างต้นเราจะเห็นว่าbarkสามารถจำลองเป็นsubresource ของ adog (เนื่องจาก a barkอยู่ในสุนัขนั่นคือเปลือกคือ "barked" โดยสุนัข)

จากเหตุผลนั้นเราได้:

  • วิธีการคือ POST
  • ทรัพยากรคือแหล่งข้อมูล/barksย่อยของ dog: /v1/dogs/1/barksซึ่งหมายถึงbark"factory" URI นั้นมีลักษณะเฉพาะสำหรับสุนัขแต่ละตัว (เนื่องจากอยู่ใต้/v1/dogs/{id})

ตอนนี้แต่ละกรณีในรายการของคุณมีพฤติกรรมที่เฉพาะเจาะจง

1. เรือสำเภาเพียงส่งอีเมลไปที่dog.emailและไม่บันทึกอะไรเลย

ประการแรกการเห่า (ส่งอีเมล) เป็นงานที่ทำข้อมูลให้ตรงกันหรืองานที่ทำข้อมูลให้ตรงกัน ประการที่สองที่ไม่barkคำขอต้องใช้เอกสารใด ๆ (e-mail, อาจจะ) หรือมันว่างเปล่า


1.1 bark ส่งอีเมลไปยังdog.emailและไม่บันทึกสิ่งใด (เป็นงานที่ทำข้อมูลให้ตรงกัน)

กรณีนี้ง่าย การเรียกใช้barksทรัพยากรของโรงงานจะให้เปลือก (ส่งอีเมล) ทันทีและจะได้รับการตอบกลับ (ถ้าตกลงหรือไม่):

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(entity-body is empty - or, if you require a **document**, place it here)

200 OK

ตามที่บันทึกไว้ (เปลี่ยนแปลง) ไม่มีอะไร200 OKเพียงพอ มันแสดงให้เห็นว่าทุกอย่างเป็นไปตามที่คาดไว้


1.2 เปลือกส่งอีเมลdog.emailและบันทึกสิ่งใด (เป็นงานอะซิงโครนัส)

ในกรณีนี้ลูกค้าจะต้องมีวิธีการติดตามbarkงาน barkงานแล้วควรจะเป็นทรัพยากรที่มีเป็นของตัวเอง URI .:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed;
NOTE: when possible, the response SHOULD contain a short hypertext note with a hyperlink
to the newly created resource (bark) URI, the same returned in the Location header
(also notice that, for the 202 status code, the Location header meaning is not
standardized, thus the importance of a hipertext/hyperlink response)}

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

ด้วยวิธีนี้แต่ละคนbarkสามารถติดตามได้ จากนั้นไคลเอ็นต์สามารถออก a GETถึงbarkURI เพื่อให้ทราบสถานะปัจจุบัน อาจใช้DELETEเพื่อยกเลิก


2. เปลือกไม้ส่งอีเมลไปที่dog.emailแล้วเพิ่มทีละdog.barkCount1

สิ่งนี้อาจมีเล่ห์เหลี่ยมถ้าคุณต้องการให้ลูกค้าทราบว่าdogทรัพยากรได้รับการเปลี่ยนแปลง:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed; when possible, containing a hipertext/hyperlink with the address
in the Location header -- says the standard}

303 See Other
Location: http://api.animals.com/v1/dogs/1

ในกรณีนี้ความตั้งใจหัวคือการปล่อยให้ลูกค้ารู้ว่าเขาควรจะดูที่location dogจากHTTP RFC เกี่ยวกับ303 :

วิธีการนี้มีอยู่เป็นหลักเพื่อให้เอาต์พุตของ POSTสคริปต์ที่เรียกทำงานเพื่อเปลี่ยนเส้นทางตัวแทนผู้ใช้ไปยังทรัพยากรที่เลือก

หากภารกิจนั้นไม่ตรงกันbarkจำเป็นต้องมีแหล่งข้อมูลย่อยเช่นเดียวกับ1.2สถานการณ์และ303ควรส่งคืนที่GET .../barks/Yเมื่องานเสร็จสมบูรณ์


3. เปลือกไม้สร้างbarkเร็กคอร์ดใหม่ " " พร้อมbark.timestampการบันทึกเมื่อเปลือกเกิดขึ้น นอกจากนี้ยังเพิ่มขึ้นทีละdog.barkCount1

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(document body, if needed)

201 Created
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

ในที่นี่barkมีการสร้างเนื่องจากคำขอดังนั้นสถานะ201 Createdจะถูกนำไปใช้

หากการสร้างแบบอะซิงโครนัส202 Acceptedจำเป็นต้องใช้a ( ตามที่ HTTP RFC บอกว่า ) แทน

การประทับเวลาที่บันทึกไว้เป็นส่วนหนึ่งของbarkทรัพยากรและสามารถเรียกดูกับGETมัน สุนัขที่ได้รับการอัพเดตสามารถ "บันทึก" ในสิ่งนั้นได้GET dogs/X/barks/Yเช่นกัน


4. เปลือกไม้เรียกใช้คำสั่งระบบเพื่อดึงรหัสสุนัขรุ่นล่าสุดลงมาจาก Github จากนั้นจะส่งข้อความตัวอักษรเพื่อdog.ownerบอกพวกเขาว่ารหัสสุนัขตัวใหม่นั้นกำลังใช้งานอยู่

ถ้อยคำของสิ่งนี้มีความซับซ้อน แต่มันค่อนข้างเป็นงานอะซิงโครนัสที่เรียบง่าย:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(document body, if needed)

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

จากนั้นลูกค้าจะออกGETs เพื่อ/v1/dogs/1/barks/a65h44ทราบสถานะปัจจุบัน (ถ้ารหัสถูกดึงมันอีเมลจะถูกส่งไปยังเจ้าของและเช่นนั้น) เมื่อใดก็ตามที่มีการเปลี่ยนแปลงสุนัขที่303เป็น appliable


ห่อ

การอ้างอิงRoy Fielding :

สิ่งเดียวที่ REST ต้องการสำหรับวิธีการคือพวกมันถูกกำหนดอย่างเท่าเทียมกันสำหรับทรัพยากรทั้งหมด (นั่นคือเพื่อให้ตัวกลางไม่จำเป็นต้องรู้ประเภทของทรัพยากรเพื่อทำความเข้าใจความหมายของคำขอ)

ในตัวอย่างข้างต้นPOSTได้รับการออกแบบอย่างสม่ำเสมอ มันจะทำให้สุนัข " bark" ไม่ปลอดภัย (หมายถึงเปลือกไม้มีผลกระทบต่อทรัพยากร) และ idempotent (คำขอแต่ละครั้งให้ผลลัพธ์ใหม่bark) ซึ่งเหมาะกับPOSTคำกริยา

โปรแกรมเมอร์จะได้รู้ว่ากPOSTจะมีผลเป็นbarks barkรหัสสถานะการตอบกลับ (รวมถึงเอนทิตีร่างกายและส่วนหัวเมื่อจำเป็น) ทำหน้าที่อธิบายสิ่งที่เปลี่ยนแปลงและวิธีที่ไคลเอ็นต์สามารถและควรดำเนินการต่อ

หมายเหตุ: แหล่งที่มาหลักที่ใช้คือ: " พักผ่อน Web Services " หนังสือที่HTTP RFCและบล็อกรอยฝ่าย




แก้ไข:

คำถามและคำตอบจึงเปลี่ยนไปเล็กน้อยตั้งแต่สร้างครั้งแรก คำถามเดิมถามเกี่ยวกับการออกแบบของ URI เช่น:

ACTION http://api.animals.com/v1/dogs/1/?action=bark

ด้านล่างนี้เป็นคำอธิบายว่าทำไมจึงไม่ใช่ตัวเลือกที่ดี:

วิธีลูกค้าบอกเซิร์ฟเวอร์ว่าจะทำอย่างไรกับข้อมูลที่เป็นข้อมูลวิธีการ

  • บริการเว็บสงบเงียบถ่ายทอดข้อมูลวิธีการในวิธี HTTP
  • บริการ RPC-Style และ SOAP ทั่วไปจะเก็บไว้ในส่วนหัวของเอนทิตีและ HTTP

ซึ่งเป็นส่วนหนึ่งของข้อมูล [ลูกค้าต้องการเซิร์ฟเวอร์] เพื่อใช้งานบนเป็นข้อมูลการกำหนดขอบเขต

  • บริการ RESTful ใช้ URI บริการ SOAP / RPC-Style ใช้ส่วนหัวของเอนทิตีและ HTTP อีกครั้ง

เป็นตัวอย่างที่ใช้ http://www.google.com/search?q=DOGURI มีข้อมูลวิธีการและข้อมูลการกำหนดขอบเขตคือGET/search?q=DOG

เรื่องสั้นสั้น:

  • ในสถาปัตยกรรม RESTfulข้อมูลวิธีการจะเข้าสู่วิธีการ HTTP
  • ในสถาปัตยกรรมเชิงทรัพยากรข้อมูลการกำหนดขอบเขตจะเข้าสู่ URI

และกฎของหัวแม่มือ:

หากวิธี HTTP ไม่ตรงกับข้อมูลวิธีแสดงว่าบริการนั้นไม่สงบ หากข้อมูลการกำหนดขอบเขตไม่ได้อยู่ใน URI บริการจะไม่มุ่งเน้นทรัพยากร

คุณสามารถใส่"เปลือก" "การกระทำ"ใน URL (หรือในกิจการที่ร่างกาย) POSTและการใช้งาน ไม่มีปัญหามีการทำงานและอาจจะเป็นวิธีที่ง่ายที่สุดที่จะทำมันแต่ไม่สงบ

เพื่อให้บริการของคุณสงบเงียบจริงๆคุณอาจต้องย้อนกลับไปและคิดเกี่ยวกับสิ่งที่คุณต้องการทำที่นี่จริง ๆ (สิ่งที่จะมีผลกระทบต่อทรัพยากร)

ฉันไม่สามารถพูดคุยเกี่ยวกับความต้องการทางธุรกิจของคุณโดยเฉพาะ แต่ให้ฉันให้คุณตัวอย่างเช่น: example.com/order/123พิจารณาการให้บริการสั่งซื้อสงบที่คำสั่งซื้อที่ยูริเหมือน

ตอนนี้บอกว่าเราต้องการยกเลิกคำสั่งซื้อเราจะทำอย่างไร หนึ่งอาจถูกล่อลวงไปคิดว่าเป็น"ยกเลิก" "การกระทำ"POST example.com/order/123?do=cancelและการออกแบบเป็น

นั่นไม่สงบเช่นที่เราพูดไปข้างต้น แต่เราอาจจะPUTเป็นตัวแทนคนใหม่ของorderที่มีcanceledองค์ประกอบที่ส่งไปยังtrue:

PUT /order/123 HTTP/1.1
Content-Type: application/xml

<order id="123">
    <customer id="89987">...</customer>
    <canceled>true</canceled>
    ...
</order>

และนั่นคือมัน หากไม่สามารถยกเลิกคำสั่งซื้อได้คุณสามารถส่งคืนรหัสสถานะเฉพาะได้ (การออกแบบแหล่งข้อมูลย่อยเช่นเดียวPOST /order/123/canceledกับเอนทิตีร่างกายtrueอาจเพื่อความเรียบง่ายนอกจากนี้ยังมี)

ในสถานการณ์เฉพาะของคุณคุณอาจลองทำสิ่งที่คล้ายกัน วิธีการที่ในขณะที่สุนัขเห่าเช่นGETที่/v1/dogs/1/อาจรวมถึงข้อมูลที่(เช่น<barking>true</barking> ) หรือ ... ถ้ามันซับซ้อนเกินไปให้คลายความต้องการ RESTful และทำPOSTตาม

ปรับปรุง:

ฉันไม่ต้องการให้คำตอบใหญ่เกินไป แต่ใช้เวลาสักครู่ในการหยุดการเปิดเผยอัลกอริทึม (การกระทำ ) เป็นชุดของทรัพยากร แทนที่จะคิดในแง่ของการกระทำ ( "ค้นหาสถานที่บนแผนที่" ) เราต้องคิดในแง่ของผลลัพธ์ของการกระทำนั้น ( "รายการสถานที่บนแผนที่ที่ตรงกับเกณฑ์การค้นหา" )

คุณอาจพบว่าตัวเองกลับมาที่ขั้นตอนนี้หากคุณพบว่าการออกแบบของคุณไม่พอดีกับอินเทอร์เฟซที่เป็นชุดของ HTTP

ตัวแปรแบบสอบถามมีการ กำหนดขอบเขตข้อมูลแต่ไม่ทรัพยากรใหม่แสดงว่า ( /post?lang=enชัดเจนเดียวกันทรัพยากรเป็น/post?lang=jpเพียงแค่การแสดงที่แตกต่างกัน) แต่พวกเขาจะใช้ในการถ่ายทอดสถานะลูกค้า (เช่น?page=10เพื่อที่รัฐจะไม่เก็บไว้ในเซิร์ฟเวอร์นั้น?lang=enเป็นตัวอย่างที่นี่) หรือพารามิเตอร์ป้อนเข้ากับทรัพยากรอัลกอริทึม ( /search?q=dogs, /dogs?code=1) อีกครั้งทรัพยากรไม่แตกต่างกัน

คุณสมบัติ HTTP verbs '(methods):

อีกจุดที่ชัดเจนที่แสดง?action=somethingใน URI ไม่ใช่สงบเป็นคุณสมบัติของคำกริยา HTTP:

  • GETและHEADปลอดภัย (และ idempotent);
  • PUTและDELETEเป็น idempotent เท่านั้น
  • POST ไม่ใช่

ความปลอดภัย : A GETหรือHEADคำขอเป็นคำขอเพื่ออ่านข้อมูลบางอย่างไม่ใช่คำขอเพื่อเปลี่ยนสถานะเซิร์ฟเวอร์ใด ๆ ลูกค้าสามารถสร้างGETหรือHEADขอ 10 ครั้งและมันก็เหมือนกับการทำครั้งเดียวหรือไม่เคยทำเลย

Idempotence : การดำเนินการ idempotent ในที่มีผลเช่นเดียวกันไม่ว่าคุณจะใช้เพียงครั้งเดียวหรือมากกว่าหนึ่งครั้ง (ในทางคณิตศาสตร์การคูณด้วยศูนย์คือ idempotent) หากคุณDELETEใช้ทรัพยากรครั้งเดียวการลบอีกครั้งจะมีผลเช่นเดียวกัน (ทรัพยากรมีGONEอยู่แล้ว)

POSTไม่ปลอดภัยหรือ idempotent POSTการส่งคำขอที่เหมือนกันสองรายการไปยังทรัพยากร 'โรงงาน' อาจส่งผลให้มีทรัพยากรรองสองรายการที่มีข้อมูลเดียวกัน ด้วยการโอเวอร์โหลด (เมธอดใน URI หรือเอนทิตี้ของ) การPOSTเดิมพันทั้งหมดจะปิด

คุณสมบัติทั้งสองนี้มีความสำคัญต่อความสำเร็จของโปรโตคอล HTTP (ผ่านเครือข่ายที่ไม่น่าเชื่อถือ!): คุณอัปเดตGETหน้าเว็บกี่ครั้งโดยไม่รอจนกว่าจะโหลดเต็ม

การสร้างการกระทำและการวางไว้ใน URL จะเป็นการแบ่งสัญญาของวิธี HTTP อย่างชัดเจน เทคโนโลยีช่วยให้คุณอีกครั้งคุณสามารถทำได้ แต่นั่นไม่ใช่การออกแบบที่สงบ


ฉันถกเถียงกับแนวคิดที่เรียกการกระทำบนเซิร์ฟเวอร์ที่กำหนดให้เป็นการกระทำใน URL นั้นไม่สงบ POSTถูกออกแบบมาสำหรับ"ให้บล็อกของข้อมูล ... กับกระบวนการจัดการข้อมูล" ดูเหมือนว่าคนจำนวนมากแยกแยะทรัพยากรออกจากการกระทำ แต่การกระทำจริงๆเป็นเพียงประเภทของทรัพยากร
Jacob Stevens

1
@JacobStevens OP เปลี่ยนคำถามเล็กน้อยดังนั้นฉันต้องอัปเดตคำตอบของฉันเพื่อให้ตรงมากขึ้น (ตรวจสอบคำถามเดิมคุณอาจเห็นว่าฉันหมายถึงอะไร) ฉันเห็นด้วยกับPOST"การให้บล็อกข้อมูล ... กับกระบวนการจัดการข้อมูล" แต่ความแตกต่างคือบล็อกข้อมูลไม่ใช่บล็อกข้อมูลและขั้นตอน (การกระทำวิธีการคำสั่ง) ดำเนินการแล้ว นั่นคือการPOSTบรรทุกเกินพิกัดและการPOSTบรรทุกเกินพิกัดคือการออกแบบสไตล์ RPC ไม่ใช่ RESTful
acdcjunior

ฉันสันนิษฐานว่าตรรกะการกระทำ / วิธีการจะถูกเก็บไว้บนเซิร์ฟเวอร์มิฉะนั้นจุดประสงค์ของการโทรจะเป็นอย่างไร ในกรณีที่คุณอธิบายฉันเห็นด้วยที่จะไม่ออกแบบที่ดี แต่เมธอดหรือรูทีนย่อยที่ดำเนินการกับการกระทำนั้นจะถูกระบุโดย URI (ซึ่งเป็นอีกสาเหตุหนึ่งที่ว่าทำไมทรัพยากรการดำเนินการที่กำหนดให้เป็นคำกริยาที่ส่วนท้ายของ URL นั้นมีประโยชน์และ RESTful
Jacob Stevens

6
คำตอบที่เราอัพเดท มันค่อนข้างนานเพราะต้องการคำอธิบายอย่างละเอียด ("โปรดจำไว้ว่าฉันมีความเข้าใจพื้นฐานเกี่ยวกับ REST") มันเป็นการดิ้นรนเพื่อทำให้มันชัดเจนสมบูรณ์ที่สุดเท่าที่จะทำได้ หวังว่ามันจะมีประโยชน์ในทางใดทางหนึ่ง
acdcjunior

2
คำอธิบายที่ดีฉันลงคะแนน แต่ไม่ควรใช้ส่วนหัว Location ในการตอบสนองที่ยอมรับ 202 ดูเหมือนจะเป็นการตีความที่ผิดที่หลายคนทำจาก RFC ตรวจสอบstackoverflow.com/questions/26199228/
Delmo

6

ฉันตอบไปก่อนหน้านี้แต่คำตอบนี้ขัดแย้งกับคำตอบเดิมของฉันและทำตามกลยุทธ์ที่แตกต่างกันมากสำหรับการหาทางออก มันแสดงให้เห็นว่าคำขอ HTTP ถูกสร้างขึ้นจากแนวคิดที่กำหนด REST และ HTTP นอกจากนี้ยังใช้PATCHแทนหรือPOSTPUT

มันต้องผ่านข้อ จำกัด REST จากนั้นส่วนประกอบของ HTTP จากนั้นจึงเป็นวิธีแก้ปัญหาที่เป็นไปได้

ส่วนที่เหลือ

REST เป็นชุดของข้อ จำกัด ที่มีวัตถุประสงค์เพื่อนำไปใช้กับระบบไฮเปอร์มีเดียแบบกระจายเพื่อให้สามารถปรับขนาดได้ แม้จะเข้าใจในบริบทของการควบคุมการกระทำจากระยะไกลคุณต้องคิดถึงการควบคุมการกระทำจากระยะไกลโดยเป็นส่วนหนึ่งของระบบสื่อหลายมิติแบบกระจายซึ่งเป็นส่วนหนึ่งของระบบสำหรับการค้นหาการดูและการแก้ไขข้อมูลที่เชื่อมโยงถึงกัน หากนั่นเป็นปัญหามากกว่าที่ควรค่าก็คงจะไม่ดีที่จะพยายามทำให้สงบ หากคุณต้องการพิมพ์ "แผงควบคุม" GUI บนไคลเอนต์ที่สามารถกระตุ้นการทำงานบนเซิร์ฟเวอร์ผ่านพอร์ต 80 คุณอาจต้องการอินเตอร์เฟส RPC แบบง่าย ๆ เช่น JSON-RPC ผ่านการร้องขอ / ตอบกลับ HTTP หรือ WebSocket

แต่ส่วนที่เหลือเป็นวิธีการคิดที่น่าสนใจและตัวอย่างในคำถามที่เกิดขึ้นจะง่ายต่อการจำลองด้วยอินเทอร์เฟซ RESTful ดังนั้นลองมาท้าทายความสนุกและการศึกษา

REST ถูกกำหนดโดยข้อ จำกัด สี่อินเตอร์เฟส:

การระบุทรัพยากร การจัดการทรัพยากรผ่านการเป็นตัวแทน ข้อความอธิบายตนเอง และไฮเปอร์มีเดียเป็นเอ็นจินของสถานะแอปพลิเคชัน

คุณถามว่าคุณสามารถกำหนดอินเทอร์เฟซได้อย่างไรตอบสนองข้อ จำกัด เหล่านี้โดยที่คอมพิวเตอร์เครื่องหนึ่งบอกคอมพิวเตอร์เครื่องอื่นให้ทำเปลือกสุนัข คุณต้องการให้อินเทอร์เฟซของคุณเป็น HTTP และคุณไม่ต้องการยกเลิกคุณลักษณะที่ทำให้ HTTP RESTful เมื่อใช้งานตามที่ต้องการ

ขอเริ่มต้นด้วยข้อ จำกัด แรก: บัตรประจำตัวทรัพยากร

ข้อมูลใด ๆ ที่สามารถตั้งชื่อนั้นสามารถเป็นทรัพยากรได้: เอกสารหรือรูปภาพบริการชั่วคราว (เช่น "สภาพอากาศในปัจจุบันในลอสแองเจลิส"), การรวบรวมทรัพยากรอื่น ๆ , วัตถุที่ไม่ใช่เสมือน (เช่นบุคคล) เป็นต้น .

ดังนั้นสุนัขจึงเป็นทรัพยากร จะต้องมีการระบุ

อีกอย่างแม่นยำทรัพยากรRเป็นชั่วคราวที่แตกต่างกันฟังก์ชั่นสมาชิกM R ( T ) ซึ่งเวลาทีแมปกับชุดของหน่วยงานหรือค่าซึ่งเทียบเท่า ค่าในชุดที่อาจจะมีการแสดงทรัพยากรและ / หรือตัวระบุทรัพยากร

คุณสร้างแบบจำลองสุนัขโดยการใช้ชุดของตัวระบุและการเป็นตัวแทนและบอกว่าพวกมันเกี่ยวข้องกันในเวลาที่กำหนด สำหรับตอนนี้เรามาใช้ตัวระบุ "dog # 1" นั่นนำเราไปสู่ข้อ จำกัด ที่สองและสาม: การแทนทรัพยากรและการอธิบายตนเอง

คอมโพเนนต์ REST ดำเนินการกับทรัพยากรโดยใช้การเป็นตัวแทนในการจับภาพสถานะปัจจุบันหรือที่ตั้งใจไว้ของทรัพยากรนั้นและถ่ายโอนการเป็นตัวแทนนั้นระหว่างส่วนประกอบ การแทนคือลำดับของไบต์รวมทั้งการแทนข้อมูลเมตาเพื่ออธิบายไบต์เหล่านั้น

ต่อไปนี้เป็นลำดับไบต์ที่จับภาพสถานะที่ตั้งใจของสุนัขนั่นคือการเป็นตัวแทนที่เราต้องการเชื่อมโยงกับตัวระบุ "dog # 1" (โปรดทราบว่ามันเป็นเพียงส่วนหนึ่งของรัฐเนื่องจากไม่คำนึงถึงชื่อสุนัขสุขภาพ หรือแม้แต่เห่าที่ผ่านมา):

มันเห่าทุก 10 นาทีตั้งแต่เวลาที่การเปลี่ยนแปลงสถานะนี้ได้รับผลกระทบและจะดำเนินต่อไปเรื่อย ๆ

มันควรจะแนบมากับข้อมูลเมตาที่อธิบาย ข้อมูลเมตานี้อาจมีประโยชน์:

มันเป็นงบภาษาอังกฤษ มันอธิบายถึงส่วนหนึ่งของรัฐที่ตั้งใจไว้ หากได้รับหลายครั้งอนุญาตให้เฉพาะคนแรกที่มีผลกระทบ

สุดท้ายให้ดูที่ข้อ จำกัด ที่สี่: HATEOAS

ส่วนที่เหลือ ... ดูแอปพลิเคชันเป็นโครงสร้างข้อมูลและตัวเลือกการควบคุมที่ผู้ใช้สามารถทำงานที่ต้องการได้ ตัวอย่างเช่นการค้นหาคำในพจนานุกรมออนไลน์เป็นแอปพลิเคชั่นเดียวเช่นเดียวกับการท่องเที่ยวผ่านพิพิธภัณฑ์เสมือนจริงหรือการตรวจสอบบันทึกย่อของชั้นเรียนเพื่อศึกษาสำหรับการสอบ ... สถานะการควบคุมถัดไปของแอปพลิเคชั่นอยู่ในการแสดงของทรัพยากรที่ร้องขอครั้งแรกดังนั้นการได้รับการแสดงครั้งแรกนั้นเป็นลำดับความสำคัญ ... โมเดลแอปพลิเคชันเป็นเครื่องมือที่ย้ายจากสถานะหนึ่งไปยังอีกสถานะหนึ่งโดยการตรวจสอบและเลือกจากการเปลี่ยนสถานะทางเลือกในชุดการแสดงปัจจุบัน

ในอินเทอร์เฟซ RESTful ไคลเอนต์ได้รับการแสดงทรัพยากรเพื่อคิดออกว่าควรรับหรือส่งการแทนค่าอย่างไร จะต้องมีตัวแทนอยู่ที่ไหนสักแห่งในแอปพลิเคชันซึ่งลูกค้าสามารถคิดได้ว่าจะรับหรือส่งการรับรองทั้งหมดที่ควรจะสามารถรับหรือส่งแม้ว่าจะเป็นไปตามสายโซ่ของการเป็นตัวแทนถึงข้อมูลนั้น ดูเหมือนง่ายพอ:

ลูกค้าขอให้ตัวแทนของทรัพยากรที่ระบุว่าเป็นหน้าแรก; ในการตอบสนองมันจะได้รับการเป็นตัวแทนที่มีตัวระบุของสุนัขทุกตัวที่ลูกค้าอาจต้องการ ลูกค้าแยกตัวระบุจากมันและถามบริการว่ามันสามารถโต้ตอบกับสุนัขที่ระบุและบริการบอกว่าลูกค้าสามารถส่งคำสั่งภาษาอังกฤษที่อธิบายถึงส่วนหนึ่งของสถานะสุนัขที่ตั้งใจไว้ จากนั้นลูกค้าจะส่งคำสั่งดังกล่าวและได้รับข้อความแสดงความสำเร็จหรือข้อความแสดงข้อผิดพลาด

HTTP

HTTP ใช้ข้อ จำกัด REST ดังนี้:

การระบุทรัพยากร : URI

การเป็นตัวแทนทรัพยากร : นิติบุคคล - ร่างกาย

การอธิบายตัวเอง : เมธอดหรือรหัสสถานะส่วนหัวและส่วนต่าง ๆ ของเอนทิตี้ของร่างกาย (เช่น URI ของ XML schema)

HATEOAS : การเชื่อมโยงหลายมิติ

คุณได้ตัดสินใจhttp://api.animals.com/v1/dogs/1เป็น URI สมมติว่าลูกค้าได้รับสิ่งนี้จากบางหน้าในเว็บไซต์

มาใช้เอนทิตี้ของร่างกายนี้ (ค่าของnextคือการประทับเวลาค่าของ0วิธีการ 'เมื่อได้รับการร้องขอนี้'):

{"barks": {"next": 0, "frequency": 10}}

ตอนนี้เราต้องการวิธีการ PATCHเหมาะกับคำอธิบาย "ส่วนหนึ่งของสถานะที่ต้องการ" ที่เราตัดสินใจ:

เมธอด PATCH ร้องขอให้ชุดของการเปลี่ยนแปลงที่อธิบายในเอนทิตีคำร้องขอถูกนำไปใช้กับรีซอร์สที่ระบุโดย Request-URI

และส่วนหัวบางส่วน:

ในการระบุภาษาของนิติบุคคล -: Content-Type: application/json

เพื่อให้แน่ใจว่าจะเกิดขึ้นเพียงครั้งเดียวเท่านั้น: If-Unmodified-Since: <date/time this was first sent>

และเรามีคำขอ:

PATCH /v1/dogs/1/ HTTP/1.1
Host: api.animals.com
Content-Type: application/json
If-Unmodified-Since: <date/time this was first sent>
[other headers]

{"barks": {"next": 0, "frequency": 10}}

ในความสำเร็จลูกค้าควรได้รับ204รหัสสถานะในการตอบสนองหรือ a 205ถ้าการเป็นตัวแทนของ/v1/dogs/1/มีการเปลี่ยนแปลงเพื่อสะท้อนให้เห็นถึงตารางเวลาเห่าใหม่

เมื่อเกิดข้อผิดพลาดควรได้รับ403ข้อความที่มีประโยชน์

ไม่จำเป็นที่ REST สำหรับบริการเพื่อสะท้อนถึงตารางการเห่าในการแสดงเพื่อตอบสนองGET /v1/dogs/1/แต่มันจะเหมาะสมที่สุดหากการแสดง JSON รวมถึงสิ่งนี้:

"barks": {
    "previous": [x_1, x_2, ..., x_n],
    "next": x_n,
    "frequency": 10
}

จัดการกับงาน cron เป็นรายละเอียดการนำไปใช้งานที่เซิร์ฟเวอร์ซ่อนจากอินเตอร์เฟส นั่นคือความงามของอินเทอร์เฟซทั่วไป ลูกค้าไม่จำเป็นต้องรู้ว่าเซิร์ฟเวอร์ทำอะไรอยู่เบื้องหลัง สิ่งที่สำคัญคือบริการเข้าใจและตอบสนองต่อการเปลี่ยนแปลงสถานะที่ร้องขอ


3

คนส่วนใหญ่ใช้POSTเพื่อจุดประสงค์นี้ เหมาะสำหรับการดำเนินการ "การดำเนินการที่ไม่ปลอดภัยหรือ nonidempotent ใด ๆ เมื่อไม่มีวิธี HTTP อื่นที่เหมาะสม"

API เช่นXMLRPCใช้POSTเพื่อทริกเกอร์การกระทำที่สามารถเรียกใช้รหัสโดยอำเภอใจ "การกระทำ" รวมอยู่ในข้อมูล POST:

POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181

<?xml version="1.0"?>
<methodCall>
   <methodName>examples.getStateName</methodName>
   <params>
      <param>
         <value><i4>41</i4></value>
         </param>
      </params>
   </methodCall>

ตัวอย่าง RPC ถูกกำหนดให้แสดงว่า POST เป็นตัวเลือกทั่วไปของคำกริยา HTTP สำหรับวิธีฝั่งเซิร์ฟเวอร์ ต่อไปนี้เป็นความคิดของRoy Fielding เกี่ยวกับ POST - เขาบอกว่ามันเงียบมากที่จะใช้วิธี HTTP ตามที่ระบุ

โปรดทราบว่า RPC นั้นไม่ได้สงบมากนักเพราะไม่ได้มุ่งเน้นทรัพยากร แต่ถ้าคุณต้องการไร้สัญชาติแคชหรือฝังรากลึกก็ไม่ยากที่จะทำการเปลี่ยนแปลงที่เหมาะสม ดูhttp://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/สำหรับตัวอย่าง


ฉันคิดว่าคุณจะใช้รหัสของพารามิเตอร์ที่ไม่ได้ใส่ไว้ในสตริงการสืบค้น
tacos_tacos_tacos

@Kirk ใช่ แต่มีการปรับเปลี่ยนเล็กน้อยหนึ่งวางสุดท้ายทับ: POST api.animals.com/v1/dogs1?action=bark
เรย์มอนด์ Hettinger

หากคุณทำตามคำแนะนำในคำตอบนี้โปรดจำไว้ว่า API ที่ได้จะไม่สงบ
Nicholas Shanks

2
สิ่งนี้ไม่ได้สงบเนื่องจาก HTTP สร้าง URL เป็นตัวระบุของทรัพยากรและ URL ของ/RPC2ไม่ทำอะไรเลยเพื่อระบุทรัพยากร - มันระบุเทคโนโลยีเซิร์ฟเวอร์ สิ่งนี้ใช้methodNameเพื่อพยายาม 'ระบุ' ทรัพยากร '- แต่ถึงกระนั้นก็ไม่ได้ประโยชน์จากคำนามที่แตกต่าง / คำกริยา; สิ่งเดียวที่ 'verb' methodCallเหมือนที่นี่เป็น นี่เป็นเหมือน 'ทำชื่อรัฐเรียกคืน' แทน 'เรียกดูสถานะชื่อ' - หลังทำให้เหมาะสมมากขึ้น
Jordan

+1 สำหรับลิงก์ ข้อมูลมากและการทดลอง "ความเห็น RPC" เป็นสิ่งประดิษฐ์
Jordan

2

POSTเป็นวิธี HTTP ที่ออกแบบมาสำหรับ

มอบบล็อกข้อมูล ... ให้กับกระบวนการจัดการข้อมูล

วิธีการด้านเซิร์ฟเวอร์การจัดการการดำเนินการที่ไม่ CRUD แมปคือสิ่งที่รอยฟีลดิงตั้งใจกับส่วนที่เหลือเพื่อให้คุณดีมีและนั่นเป็นเหตุผลที่POSTถูกสร้างขึ้นมาให้เป็นที่ไม่ใช่ idempotent POSTจะจัดการการโพสต์ข้อมูลส่วนใหญ่ไปยังวิธีฝั่งเซิร์ฟเวอร์เพื่อประมวลผลข้อมูล

ถ้าคุณต้องการให้เห่าฝั่งเซิร์ฟเวอร์ดำเนินการทุก ๆ 10 นาที แต่ด้วยเหตุผลบางอย่างที่ต้องการทริกเกอร์ให้เริ่มต้นจากไคลเอนต์PUTก็จะตอบสนองวัตถุประสงค์ได้ดีกว่า อย่างดีจากสถานการณ์นี้ไม่มีความเสี่ยงที่ชัดเจนในการร้องขอ POST หลายครั้งที่ทำให้สุนัขของคุณเหมียวแทน คำตอบของฉันสำหรับคำถาม SO ที่คล้ายกันอาจมีประโยชน์สำหรับคุณ


1
PUT vs. POST เป็นข้อมูลเกี่ยวกับ URL ย่อหน้าที่สามหลังจาก9.6 PUTกล่าวว่าจุดประสงค์ของทั้งสองวิธีคือPUTURL อ้างอิงถึงสิ่งที่ควรถูกแทนที่ด้วยเนื้อหาของลูกค้าและPOSTURL อ้างอิงถึงสิ่งที่ควรประมวลผลเนื้อหาของลูกค้า แต่ต้องการ
Jordan

1

หากเราถือว่า Barking เป็นทรัพยากรภายใน / ขึ้นอยู่กับ / ซึ่งผู้บริโภคสามารถดำเนินการได้เราก็สามารถพูดได้ว่า:

POST http://api.animals.com/v1/dogs/1/bark

สุนัขจำนวน 1 เปลือก

GET http://api.animals.com/v1/dogs/1/bark

ส่งคืนการประทับเวลาของเปลือกไม้ครั้งสุดท้าย

DELETE http://api.animals.com/v1/dogs/1/bark

ใช้ไม่ได้! ดังนั้นไม่ต้องสนใจมัน


นี่เป็นเพียงการสงบถ้าคุณพิจารณาว่า/v1/dogs/1/barkเป็นทรัพยากรต่อและPOSTเป็นคำอธิบายว่าสถานะภายในของทรัพยากรนั้นควรเปลี่ยนแปลงอย่างไร ฉันพบว่ามันสมเหตุสมผลมากกว่าที่จะพิจารณา/v1/dogs/1/ว่าเป็นทรัพยากรและเพื่อระบุในองค์กรที่ควรจะเห่า
Jordan

mmm .. เป็นทรัพยากรที่คุณสามารถเปลี่ยนสถานะได้ เนื่องจากผลลัพธ์ของการเปลี่ยนสถานะทำให้เสียงไม่ทำให้ทรัพยากรน้อยลง! คุณกำลังมองหาที่ Bark เป็นคำกริยา (ซึ่งก็คือ) นั่นคือสาเหตุที่คุณไม่สามารถพิจารณาว่าเป็นทรัพยากร ฉันมองว่ามันเป็นทรัพยากรที่พึ่งพาซึ่งรัฐสามารถเปลี่ยนแปลงได้และเนื่องจากสถานะของมันเป็นบูลีนฉันไม่เห็นเหตุผลที่จะพูดถึงมันในเอนทิตี้ของร่างกาย นั่นเป็นเพียงความคิดเห็นของฉัน
bolbol

1

การแก้ไขคำตอบบางส่วนก่อนหน้านี้แนะนำให้คุณใช้ RPC คุณไม่จำเป็นต้องมองไปที่ RPC เพราะเป็นไปได้อย่างสมบูรณ์แบบที่จะทำสิ่งที่คุณต้องการในขณะที่ปฏิบัติตามข้อ จำกัด ของ REST

ประการแรกอย่าใส่พารามิเตอร์การกระทำลงใน URL URL จะกำหนดสิ่งที่คุณใช้ในการดำเนินการและพารามิเตอร์การสืบค้นนั้นเป็นส่วนหนึ่งของ URL มันควรจะคิดว่าเป็นคำนามทั้งหมด http://api.animals.com/v1/dogs/1/?action=barkเป็นทรัพยากรที่แตกต่างกัน - คำนามที่แตกต่างกัน - http://api.animals.com/v1/dogs/1/เพื่อ [nb เจ้าของได้ออก?action=barkURI จากคำถาม.] ตัวอย่างเช่นการเปรียบเทียบที่จะhttp://api.animals.com/v1/dogs/?id=1 http://api.animals.com/v1/dogs/?id=2ทรัพยากรที่แตกต่างที่แตกต่างโดยเฉพาะสตริงแบบสอบถาม ดังนั้นการกระทำของคำขอของคุณเว้นแต่จะตรงกับประเภทวิธีการที่มีอยู่ร่างกาย (TRACE, OPTIONS, HEAD, GET, DELETE, etc) จะต้องกำหนดไว้ในเนื้อหาของคำขอ

ถัดไปตัดสินใจว่าการกระทำนั้นเป็น " idempotent " ซึ่งหมายความว่ามันสามารถทำซ้ำได้โดยไม่มีผลเสีย (ดูย่อหน้าถัดไปสำหรับคำอธิบายเพิ่มเติม) ตัวอย่างเช่นการตั้งค่าเป็นจริงสามารถทำซ้ำได้หากลูกค้าไม่แน่ใจว่าเกิดผลที่ต้องการ พวกเขาส่งคำขออีกครั้งและมูลค่ายังคงเป็นจริง การเพิ่ม 1 ไปยังหมายเลขไม่ใช่ idempotent หากไคลเอนต์ส่งคำสั่ง Add1 ไม่แน่ใจว่ามันใช้งานได้และส่งอีกครั้งเซิร์ฟเวอร์ได้เพิ่มหนึ่งหรือสองหรือไม่ เมื่อคุณพิจารณาแล้วว่าคุณอยู่ในตำแหน่งที่ดีกว่าที่จะเลือกระหว่างPUTและPOSTสำหรับวิธีการของคุณ

Idempotent หมายถึงคำขอสามารถทำซ้ำได้โดยไม่ต้องเปลี่ยนผลลัพธ์ ผลกระทบเหล่านี้ไม่รวมถึงการบันทึกและกิจกรรมอื่น ๆ ของผู้ดูแลเซิร์ฟเวอร์ การใช้ตัวอย่างแรกและครั้งที่สองของคุณการส่งอีเมลสองฉบับไปยังบุคคลคนเดียวกันนั้นส่งผลให้มีสถานะที่แตกต่างจากการส่งอีเมลหนึ่งฉบับ (ผู้รับมีสองในกล่องจดหมายซึ่งพวกเขาอาจถือว่าเป็นสแปม) ดังนั้นฉันจึงใช้ POST . หาก barkCount ในตัวอย่างที่ 2 มีวัตถุประสงค์เพื่อให้ผู้ใช้ API ของคุณเห็นหรือส่งผลต่อสิ่งที่มองเห็นได้ลูกค้าก็เป็นสิ่งที่จะทำให้คำขอไม่ใช่ idempotent หากคุณเท่านั้นที่จะดูจากนั้นจะนับเป็นการบันทึกเซิร์ฟเวอร์และควรละเว้นเมื่อยับยั้ง idempotentcy

สุดท้ายตรวจสอบว่าการกระทำที่คุณต้องการสามารถคาดหวังว่าจะประสบความสำเร็จในทันทีหรือไม่ BarkDog เป็นการกระทำที่เสร็จสมบูรณ์อย่างรวดเร็ว RunMarathon ไม่ใช่ หากการดำเนินการของคุณช้าให้พิจารณาส่งคืน202 Acceptedพร้อม URL ในส่วนการตอบกลับเพื่อให้ผู้ใช้สำรวจความคิดเห็นเพื่อดูว่าการดำเนินการเสร็จสมบูรณ์หรือไม่ อีกทางหนึ่งคือให้ผู้ใช้ POST ไปยัง URL รายการที่ต้องการ/marathons-in-progress/และเมื่อดำเนินการเสร็จแล้วให้เปลี่ยนเส้นทางผู้ใช้จาก URL ID กำลังดำเนินการไปยัง/marathons-complete/URL
สำหรับกรณีเฉพาะ # 1 และ # 2 ฉันจะให้เซิร์ฟเวอร์โฮสต์คิวและลูกค้าโพสต์ชุดของที่อยู่ การกระทำนั้นจะไม่ใช่ SendEmail แต่เป็นสิ่งที่คล้ายกับ AddToDispatchQueue เซิร์ฟเวอร์สามารถโพลคิวเพื่อดูว่ามีที่อยู่อีเมลใด ๆ ที่รออยู่และส่งอีเมลหากพบว่ามีหรือไม่ จากนั้นจะอัพเดตคิวเพื่อระบุว่าการดำเนินการที่รอดำเนินการได้ถูกดำเนินการแล้ว คุณจะให้ URI อื่นแสดงสถานะปัจจุบันของคิว เพื่อหลีกเลี่ยงการส่งอีเมลซ้ำซ้อนเซิร์ฟเวอร์สามารถเก็บบันทึกว่าใครเป็นคนส่งอีเมลนี้ไปที่ใดและตรวจสอบแต่ละที่อยู่เพื่อตรวจสอบว่าไม่มีการส่งสองครั้งไปยังที่อยู่เดียวกันแม้ว่าคุณจะโพสต์รายการเดียวกันสองครั้ง คิว

เมื่อเลือก URI สำหรับสิ่งใดก็ตามให้ลองนึกถึงผลลัพธ์แทนการกระทำ ตัวอย่างเช่นgoogle.com/search?q=dogsแสดงผลลัพธ์ของการค้นหาคำว่า "Dogs" ไม่จำเป็นต้องทำการค้นหา

เคส # 3 และ # 4 จากรายการของคุณก็ไม่ใช่การกระทำของ idempotent คุณแนะนำว่าเอฟเฟกต์ต่าง ๆ ที่แนะนำอาจส่งผลต่อการออกแบบ API ในทั้งสี่กรณีฉันจะใช้ API เดียวกันเนื่องจากทั้งสี่เปลี่ยน "สถานะโลก"


สมมติว่าการกระทำคือการปั่นผ่านคิวอีเมลขนาดใหญ่และส่งข้อความไปยังกลุ่มคน idempotent นั่นคืออะไร? การกระทำ idempotent สำหรับ PUT หรือ POST หรือไม่
Kirk Ouimet

@kirk ฉันได้ขยายคำตอบของฉัน
Nicholas Shanks

0

ดูคำตอบใหม่ของฉัน - มันขัดแย้งกับคำตอบนี้และอธิบาย REST และ HTTP อย่างชัดเจนและแม่นยำยิ่งขึ้น

นี่คือคำแนะนำที่เกิดขึ้นกับ RESTful แต่ไม่ได้เป็นตัวเลือกเดียวเท่านั้น ในการเริ่มเห่าเมื่อบริการได้รับคำขอ:

POST /v1/dogs/1/bark-schedule HTTP/1.1
...
{"token": 12345, "next": 0, "frequency": 10}

token เป็นหมายเลขโดยพลการที่ป้องกันไม่ให้เห่าซ้ำซ้อนไม่ว่าจะมีการส่งคำขอนี้กี่ครั้งก็ตาม

nextระบุเวลาของเปลือกไม้ถัดไป ค่าเฉลี่ยของ0'ASAP'

เมื่อใดก็ตามที่คุณGET /v1/dogs/1/bark-scheduleคุณควรจะได้รับบางสิ่งบางอย่างเช่นนี้ที่เสื้อเป็นเวลาของเปลือกที่ผ่านมาและยูคือT + 10 นาที:

{"last": t, "next": u}

ฉันขอแนะนำให้คุณใช้ URL เดียวกันเพื่อขอเห่าที่คุณใช้เพื่อค้นหาข้อมูลเกี่ยวกับสถานะการเห่าของสุนัขในปัจจุบัน มันไม่จำเป็นสำหรับ REST แต่เน้นการกระทำของการปรับเปลี่ยนตาราง

รหัสสถานะที่เหมาะสมน่าจะเป็น205 ฉันจินตนาการว่าลูกค้าที่ดูตารางเวลาปัจจุบันPOSTไปที่ URL เดียวกันเพื่อเปลี่ยนและได้รับคำสั่งจากบริการเพื่อให้กำหนดการดูครั้งที่สองเพื่อพิสูจน์ว่ามีการเปลี่ยนแปลง

คำอธิบาย

ส่วนที่เหลือ

ลืม HTTP ไปสักครู่ มันเป็นสิ่งสำคัญที่จะเข้าใจว่าทรัพยากรที่เป็นฟังก์ชั่นที่ต้องใช้เวลาเป็น input และส่งกลับชุดที่มีตัวบ่งชี้และการแสดง ให้ง่ายขึ้นว่า: ทรัพยากรคือชุดRของตัวระบุและการเป็นตัวแทน Rสามารถเปลี่ยนแปลงได้ - สมาชิกสามารถเพิ่มลบหรือแก้ไขได้ (แม้ว่ามันจะไม่ดี, การออกแบบที่ไม่แน่นอนที่จะลบหรือแก้ไขตัวระบุ.) เรากล่าวระบุว่าเป็นองค์ประกอบของนักวิจัยระบุRและที่เป็นตัวแทนที่เป็นองค์ประกอบของRหมายถึงR

สมมุติว่าRเป็นสุนัข คุณเกิดขึ้นเพื่อแจ้งR/v1/dogs/1เป็น (ความหมาย/v1/dogs/1เป็นสมาชิกของR. ) นั่นเป็นเพียงหนึ่งในหลายวิธีที่คุณสามารถระบุRได้ นอกจากนี้คุณยังสามารถระบุRเป็นและเป็น/v1/dogs/1/x-rays/v1/rufus

คุณเป็นตัวแทนของR ได้อย่างไร อาจจะมีรูปถ่าย อาจจะมีชุดของรังสีเอกซ์ หรืออาจมีการระบุวันที่และเวลาเมื่อRเห่าครั้งสุดท้าย แต่จำไว้ว่าสิ่งเหล่านี้เป็นตัวแทนทั้งหมดของทรัพยากรเดียวกัน /v1/dogs/1/x-raysคือตัวบ่งชี้ของทรัพยากรเดียวกันที่แสดงโดยคำตอบสำหรับคำถาม "เมื่อRไม่เห่าครั้งสุดท้าย?"

HTTP

การรับรองทรัพยากรหลายรายการนั้นไม่มีประโยชน์อย่างมากหากคุณไม่สามารถอ้างถึงแหล่งข้อมูลที่คุณต้องการ นั่นเป็นเหตุผลที่เป็นประโยชน์ HTTP: จะช่วยให้คุณระบุที่เชื่อมต่อกับการแสดง นั่นคือเป็นวิธีที่บริการจะได้รับ URL และตัดสินใจว่าการแสดงใดที่จะให้บริการกับลูกค้า

อย่างน้อยนั่นคือสิ่งที่GETทำ PUTโดยทั่วไปแล้วสิ่งที่ตรงกันข้ามGETคือ: คุณPUTเป็นตัวแทนrที่ URL หากคุณต้องการให้GETคำขอในอนาคตไปยัง URL นั้นกลับมาเป็นrด้วยการแปลที่เป็นไปได้เช่น JSON ถึง HTML

POSTเป็นวิธีที่หลวมของการแก้ไขการเป็นตัวแทน คิดว่ามีการแสดงตรรกะและตรรกะการปรับเปลี่ยนที่เป็นคู่กัน - ทั้งสอดคล้องกับ URL เดียวกัน คำขอ POST คือคำขอสำหรับตรรกะการปรับเปลี่ยนเพื่อประมวลผลข้อมูลและแก้ไขการรับรองใด ๆ (ไม่ใช่แค่การแสดงที่อยู่ใน URL เดียวกัน) ตามที่บริการเห็นว่าเหมาะสม ให้ความสนใจกับย่อหน้าที่สามหลังจาก9.6 PUT : คุณไม่ได้แทนที่สิ่งที่ URL ด้วยเนื้อหาใหม่ คุณกำลังถามสิ่งที่ URL เพื่อประมวลผลข้อมูลบางส่วนและตอบสนองอย่างชาญฉลาดในรูปแบบของข้อมูลที่เป็นตัวแทน

ในกรณีของเราเราขอให้ตรรกะการแก้ไขที่/v1/dogs/1/bark-schedule(ซึ่งเป็นคู่กับตรรกะการแสดงผลที่บอกเราเมื่อมันเห่าครั้งสุดท้ายและเมื่อมันจะเห่าถัดไป) ในการประมวลผลข้อมูลของเราและแก้ไขการรับรองบางอย่างตาม ในการตอบสนองต่ออนาคตGETตรรกะการแสดงผลที่สอดคล้องกับ URL เดียวกันจะบอกเราว่าสุนัขเห่าตอนนี้ตามที่เราต้องการ

คิดว่างาน cron เป็นรายละเอียดการใช้งาน ข้อเสนอ HTTP ในการดูและแก้ไขการเป็นตัวแทน จากนี้ไปบริการจะแจ้งลูกค้าเมื่อสุนัขเห่าครั้งสุดท้ายและเมื่อไหร่จะเห่าต่อไป จากมุมมองของการบริการนั่นคือความซื่อสัตย์เพราะเวลานั้นสอดคล้องกับงาน cron ในอดีตและที่วางแผนไว้


-1

REST เป็นมาตรฐานที่มุ่งเน้นทรัพยากรไม่ใช่การดำเนินการที่ขับเคลื่อนด้วย RPC

หากคุณต้องการให้เซิร์ฟเวอร์ของคุณเห่าคุณควรมองหาแนวคิดที่แตกต่างกันเช่นJSON-RPCหรือเป็นการสื่อสารผ่านเว็บ

ทุกความพยายามที่จะรักษามันสงบจะล้มเหลวในความคิดของฉัน: คุณสามารถออกPOSTกับactionพารามิเตอร์คุณไม่ได้สร้างทรัพยากรใหม่ใด ๆ แต่ในขณะที่คุณอาจมีผลข้างเคียงคุณจะปลอดภัยกว่า


POSTถูกออกแบบมาสำหรับ"ให้บล็อกของข้อมูล ... กับกระบวนการจัดการข้อมูล" ดูเหมือนว่าคนจำนวนมากแยกแยะทรัพยากรออกจากการกระทำ แต่การกระทำจริงๆเป็นเพียงประเภทของทรัพยากร การเรียกใช้ทรัพยากรการดำเนินการบนเซิร์ฟเวอร์ยังคงเป็นอินเทอร์เฟซแบบสม่ำเสมอแคชโมดูลาร์และปรับขนาดได้ นอกจากนี้ยังไร้สัญชาติ แต่อาจถูกละเมิดหากลูกค้าได้รับการออกแบบให้คาดหวังการตอบสนอง แต่เรียกเป็น "โมฆะวิธี" บนเซิร์ฟเวอร์เป็นสิ่งที่รอยฟีลดิงตั้งใจกับส่วนที่เหลือ
Jacob Stevens

ตามที่ฉันอธิบายในคำตอบของฉันคุณสามารถในส่วนที่เหลือโดยปริยายทำให้เซิร์ฟเวอร์เพื่อดำเนินการโดยขอให้มันพูดต่อจากนี้ไป "การกระทำของคุณเสร็จสมบูรณ์" ในขณะที่ RPC ขึ้นอยู่กับความคิดของการขอเซิร์ฟเวอร์ การกระทำ ทั้งสองทำให้รู้สึกสมบูรณ์แบบเช่นเดียวกับการเขียนโปรแกรมที่จำเป็นและการประกาศทั้งสองทำให้รู้สึก
Jordan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.