การทำธุรกรรมในส่วนที่เหลือ?


147

ฉันสงสัยว่าคุณจะใช้งานกรณีการใช้งานต่อไปนี้ได้อย่างไรใน REST มันเป็นไปได้ที่จะทำโดยไม่สูญเสียรูปแบบแนวคิด?

อ่านหรืออัปเดตหลายทรัพยากรภายในขอบเขตของธุรกรรมเดียว ตัวอย่างเช่นโอนเงิน $ 100 จากบัญชีธนาคารของ Bob ไปยังบัญชีของ John

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

คำตอบ:


91

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


18
ทำไม TransferMoneyTransaction ถึงไม่ใช่ทรัพยากรธนาคารที่ทำงานได้?
Darrel Miller

8
หากคุณมั่นใจว่าปลายทางของคุณอ้างถึงคำนามมันมักจะใช้สัญชาตญาณของ GET, PUT, POST, DELETE คำกริยามาตรฐาน RPC ช่วยให้ปลายทางสามารถใช้คำกริยาได้เองและดังนั้นจึงสามารถขัดแย้งกับคำกริยา HTTP และเจตนาที่สับสนได้
Darrel Miller

10
เช่นจะเกิดอะไรขึ้นถ้าคุณทำ HTTP DELETE บน endpoint UpdateXYZ ลบ XYZ หรือไม่ มันลบการอัปเดตหรือเพียงแค่ทำการอัปเดตและเพิกเฉยต่อการลบกริยา HTTP โดยการป้องกันคำกริยาจากจุดสิ้นสุดคุณลบความสับสน
Darrel Miller

5
แล้วธุรกรรมต่าง ๆ ในบริการที่หลากหลาย? และสิ่งที่เกี่ยวกับเมื่อคุณต้องการชุดของการเปลี่ยนแปลง 'ที่ไม่เกี่ยวข้อง' ที่บริการไม่เปิดเผยคอนเทนเนอร์ธุรกรรมโดยนัย .. ทำไมเรามีประเภทธุรกรรมเฉพาะเมื่อเราย้ายไปที่ธุรกรรมทั่วไปที่ไม่เกี่ยวข้องกับข้อมูลจริงของคุณอย่างสมบูรณ์ การเปลี่ยนแปลง ธุรกรรมอาจไม่ตรงกับความสงบ แต่ดูเหมือนว่าธุรกรรมควรจะอยู่ในชั้นบนสุดซึ่งไม่เกี่ยวข้องกับการโทรที่เหลือนอกเหนือจากข้อเท็จจริงที่ว่าส่วนหัวคำขอจะมีการอ้างอิงธุรกรรม
meandmycode

4
@meandmycode ธุรกรรมฐานข้อมูลควรอยู่ด้านหลังส่วนต่อประสาน REST อีกวิธีหนึ่งคุณสามารถแสดงธุรกรรมทางธุรกิจ (ไม่ใช่ธุรกรรมฐานข้อมูล) เป็นทรัพยากรในตัวเองแล้วคุณต้องดำเนินการชดเชยในกรณีที่เกิดความล้มเหลว
Darrel Miller

60

มีบางกรณีที่สำคัญที่ไม่ได้รับคำตอบสำหรับคำถามนี้ซึ่งฉันคิดว่าแย่เกินไปเพราะมีอันดับสูงใน Google สำหรับคำค้นหา :-)

โดยเฉพาะอย่างยิ่งสิ่งที่ดีคือ: หากคุณโพสต์สองครั้ง (เพราะแคชบางส่วนถูก hiccupped ในระดับกลาง) คุณไม่ควรถ่ายโอนจำนวนสองครั้ง

ในการเข้าถึงสิ่งนี้คุณสร้างธุรกรรมเป็นวัตถุ สิ่งนี้อาจมีข้อมูลทั้งหมดที่คุณรู้อยู่แล้วและทำให้ธุรกรรมอยู่ในสถานะรอดำเนินการ

POST /transfer/txn
{"source":"john's account", "destination":"bob's account", "amount":10}

{"id":"/transfer/txn/12345", "state":"pending", "source":...}

เมื่อคุณมีธุรกรรมนี้แล้วคุณสามารถกระทำได้เช่น:

PUT /transfer/txn/12345
{"id":"/transfer/txn/12345", "state":"committed", ...}

{"id":"/transfer/txn/12345", "state":"committed", ...}

โปรดทราบว่าการใส่หลายครั้งไม่สำคัญในตอนนี้ แม้แต่ GET บน txn ก็จะกลับมาเป็นสถานะปัจจุบัน โดยเฉพาะ PUT ตัวที่สองจะตรวจพบว่าตัวแรกอยู่ในสถานะที่เหมาะสมและเพียงแค่คืนมัน - หรือถ้าคุณพยายามที่จะใส่มันเข้าไปในสถานะ "ย้อนกลับ" หลังจากที่มันอยู่ในสถานะ "มุ่งมั่น" คุณจะได้รับ ข้อผิดพลาดและการทำธุรกรรมที่เกิดขึ้นจริงกลับมา

ตราบใดที่คุณพูดคุยกับฐานข้อมูลเดียวหรือฐานข้อมูลที่มีการตรวจสอบธุรกรรมแบบรวมกลไกนี้จะใช้งานได้จริง นอกจากนี้คุณยังอาจแนะนำการใช้เวลานอกสำหรับการทำธุรกรรมเพิ่มเติมซึ่งคุณสามารถทำได้โดยใช้ส่วนหัว Expires หากคุณต้องการ


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

33

ในเงื่อนไข REST รีซอร์สเป็นคำนามที่สามารถใช้กับคำกริยา CRUD (สร้าง / อ่าน / อัพเดต / ลบ) เนื่องจากไม่มีคำกริยา "โอนเงิน" เราจำเป็นต้องกำหนดทรัพยากร "ธุรกรรม" ที่สามารถดำเนินการได้ด้วย CRUD นี่คือตัวอย่างใน HTTP + POX ขั้นตอนแรกคือการสร้าง (วิธี HTTP POST) ธุรกรรมที่ว่างเปล่าใหม่:

POST /transaction

ส่งคืน ID ธุรกรรมเช่น "1234" และ URL ตาม "/ transaction / 1234" โปรดทราบว่าการโพสต์โพสต์นี้หลายครั้งจะไม่สร้างธุรกรรมเดียวกันที่มีหลาย ID และหลีกเลี่ยงการแนะนำสถานะ "รอดำเนินการ" นอกจากนี้ POST ไม่สามารถเป็น idempotent ได้เสมอ (ข้อกำหนด REST) ​​ดังนั้นจึงเป็นวิธีปฏิบัติที่ดีในการลดข้อมูลใน POST

คุณสามารถปล่อยการสร้าง ID ธุรกรรมให้กับลูกค้าได้ ในกรณีนี้คุณจะ POST / transaction / 1234 เพื่อสร้างธุรกรรม "1234" และเซิร์ฟเวอร์จะส่งคืนข้อผิดพลาดหากมีอยู่แล้ว ในการตอบสนองข้อผิดพลาดเซิร์ฟเวอร์สามารถส่งคืน ID ที่ไม่ได้ใช้ในปัจจุบันด้วย URL ที่เหมาะสม ไม่ใช่ความคิดที่ดีในการค้นหาเซิร์ฟเวอร์สำหรับรหัสใหม่ด้วยวิธี GET เนื่องจาก GET ไม่ควรเปลี่ยนสถานะเซิร์ฟเวอร์และการสร้าง / จองรหัสใหม่จะเปลี่ยนสถานะเซิร์ฟเวอร์

ถัดไปเราอัปเดต (วิธี PUT HTTP) การทำธุรกรรมด้วยข้อมูลทั้งหมดโดยปริยาย:

PUT /transaction/1234
<transaction>
  <from>/account/john</from>
  <to>/account/bob</to>
  <amount>100</amount>
</transaction>

หากธุรกรรมที่มี ID "1234" เป็น PUT มาก่อนเซิร์ฟเวอร์จะให้การตอบสนองข้อผิดพลาดมิฉะนั้นการตอบสนองตกลงและ URL เพื่อดูธุรกรรมที่เสร็จสมบูรณ์

หมายเหตุ: ใน / account / john "john" ควรเป็นหมายเลขบัญชีที่ไม่ซ้ำกันของ John


4
การเทียบ REST กับ CRUD เป็นข้อผิดพลาดร้ายแรง POST ไม่จำเป็นต้องหมายถึง CREATE

12
ข้อผิดพลาดร้ายแรง? ฉันรู้ว่ามีความแตกต่างระหว่าง PUT และ POST แต่มีการแมปแบบหลวม ๆ กับ CRUD "อย่างจริงจัง"?
Ted Johnson

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

20

เป็นคำถามที่ดี REST ส่วนใหญ่จะอธิบายด้วยตัวอย่างเช่นฐานข้อมูลซึ่งมีการจัดเก็บอัปเดตดึงข้อมูลลบ มีตัวอย่างบางอย่างเช่นนี้ที่เซิร์ฟเวอร์ควรประมวลผลข้อมูลในบางวิธี ฉันไม่คิดว่า Roy Fielding จะรวมสิ่งใดในวิทยานิพนธ์ของเขาซึ่งอ้างอิงจาก http หลังจากทั้งหมด

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

ฉันคิดเกี่ยวกับเรื่องนี้และดูเหมือนว่าฉันมีเหตุผลที่จะให้เซิร์ฟเวอร์ประมวลผลบางอย่างสำหรับคุณเมื่อคุณอัปโหลดเซิร์ฟเวอร์จะสร้างทรัพยากรที่เกี่ยวข้องโดยอัตโนมัติและให้ลิงก์แก่พวกเขา (ในความเป็นจริงมันจะไม่ ไม่จำเป็นต้องสร้างมันโดยอัตโนมัติ: มันสามารถบอกลิงก์ให้คุณได้และมันจะสร้างมันเมื่อไหร่และถ้าคุณติดตามพวกเขา - การสร้างแบบขี้เกียจ) และเพื่อให้ลิงก์แก่คุณเพื่อสร้างทรัพยากรที่เกี่ยวข้องใหม่ - ทรัพยากรที่เกี่ยวข้องมี URI เดียวกัน แต่ยาวกว่า (เพิ่มคำต่อท้าย) ตัวอย่างเช่น:

  1. คุณอัปโหลด ( POST ) การแสดงแนวคิดของการทำธุรกรรมด้วยข้อมูลทั้งหมด ดูเหมือนว่าจะเป็นการเรียก RPC แต่เป็นการสร้าง "ทรัพยากรธุรกรรมที่เสนอ" จริงๆ เช่น URI: ข้อ/transaction บกพร่องจะทำให้เกิดทรัพยากรหลายอย่างที่จะถูกสร้างขึ้นแต่ละอันมี URI ที่แตกต่างกัน
  2. การตอบสนองของเซิร์ฟเวอร์ระบุ URI ของทรัพยากรที่สร้างขึ้นซึ่งเป็นตัวแทนซึ่งรวมถึงลิงค์ ( URI ) เพื่อสร้างทรัพยากรที่เกี่ยวข้องของ"ทรัพยากรธุรกรรมที่ได้รับมอบหมาย" ใหม่ แหล่งข้อมูลอื่น ๆ ที่เกี่ยวข้องคือลิงค์เพื่อลบธุรกรรมที่เสนอ เหล่านี้เป็นสถานะในเครื่องสถานะซึ่งลูกค้าสามารถปฏิบัติตาม เหตุผลเหล่านี้เป็นส่วนหนึ่งของทรัพยากรที่สร้างขึ้นบนเซิร์ฟเวอร์เกินกว่าข้อมูลที่ลูกค้าให้มา เช่นยูริ: /transaction/1234/proposed, /transaction/1234/committed
  3. คุณPOSTไปที่ลิงก์เพื่อสร้าง "ทรัพยากรธุรกรรมที่ได้รับมอบหมาย"ซึ่งสร้างทรัพยากรนั้นโดยเปลี่ยนสถานะของเซิร์ฟเวอร์ (ยอดคงเหลือของทั้งสองบัญชี) ** โดยธรรมชาติแล้วทรัพยากรนี้สามารถสร้างได้เพียงครั้งเดียวเท่านั้นและไม่สามารถอัปเดตได้ ดังนั้นความผิดพลาดที่เกิดจากการทำธุรกรรมจำนวนมากไม่สามารถเกิดขึ้นได้
  4. คุณสามารถรับทรัพยากรทั้งสองนี้เพื่อดูว่ารัฐของพวกเขาคืออะไร สมมติว่า POST สามารถเปลี่ยนทรัพยากรอื่น ๆ ข้อเสนอจะถูกตั้งค่าสถานะเป็น "มุ่งมั่น" (หรืออาจจะไม่สามารถใช้ได้เลย)

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

ในธุรกรรมจริง / การขายมักมีเอกสารทางกายภาพที่แตกต่างกันสำหรับขั้นตอนต่าง ๆ ของการทำธุรกรรม (ข้อเสนอใบสั่งซื้อใบเสร็จรับเงิน ฯลฯ ) มากยิ่งขึ้นสำหรับการซื้อบ้านด้วยการตั้งถิ่นฐานเป็นต้น

OTOH มันให้ความรู้สึกเหมือนกับเล่นความหมายกับฉัน; ฉันรู้สึกไม่สบายใจกับการเปลี่ยนคำกริยาให้เป็นคำนามเพื่อทำให้มันสงบ "เพราะมันใช้คำนาม (URIs) แทนคำกริยา (RPC call)" คือคำนาม "ทรัพยากรธุรกรรมที่ได้รับมอบหมาย" แทนคำกริยา "ส่งมอบธุรกรรมนี้" ฉันเดาว่าข้อดีอย่างหนึ่งของการตั้งชื่อคือคุณสามารถอ้างถึงทรัพยากรตามชื่อแทนที่จะต้องระบุในวิธีอื่น (เช่นการรักษาสถานะเซสชันดังนั้นคุณจึงรู้ว่าธุรกรรม "นี่" คืออะไร)

แต่คำถามที่สำคัญคือประโยชน์ของวิธีนี้คืออะไร เช่น REST สไตล์นี้ดีกว่าสไตล์ RPC ในทางใดบ้าง เทคนิคที่ยอดเยี่ยมสำหรับหน้าเว็บเป็นประโยชน์สำหรับการประมวลผลข้อมูลนอกเหนือจากร้านค้า / ดึง / อัปเดต / ลบหรือไม่ ฉันคิดว่าประโยชน์หลักของ REST คือความยืดหยุ่น แง่มุมหนึ่งของการที่ไม่จำเป็นต้องรักษาสถานะของลูกค้าอย่างชัดเจน (แต่ทำให้มันโดยปริยายใน URI ของทรัพยากรและรัฐต่อไปเป็นลิงค์ในการเป็นตัวแทนของมัน) ในแง่ที่มันช่วย บางทีนี่อาจช่วยในการฝังรากลึก / การวางท่อเกินไป OTOH ผู้ใช้เพียงรายเดียวเท่านั้นที่จะดูธุรกรรมที่เฉพาะเจาะจงของพวกเขาดังนั้นจึงไม่มีข้อได้เปรียบในการแคชเพื่อให้ผู้อื่นสามารถอ่านได้ซึ่งถือเป็นชัยชนะครั้งใหญ่ของ http


คุณช่วยอธิบายได้ว่า "ไม่ต้องการรักษาสถานะของลูกค้า" ช่วยให้ขยายได้อย่างไร ความยืดหยุ่นแบบใด ความสามารถในการปรับขนาดในสิ่งที่เหมาะสม?
jhegedus

11

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

โดยสรุปหากคุณกำลังทำธุรกรรมจำนวนมาก (ประเภทไม่ใช่อินสแตนซ์) ในแอปพลิเคชันของคุณคุณไม่ควรสร้าง RESTful API


9
ใช่ แต่สิ่งที่ควรเป็นทางเลือกในกรณีของสถาปัตยกรรมไมโครเซอร์วิสแบบกระจาย?
Vitamon

11

ฉันลอยออกจากหัวข้อนี้เป็นเวลา 10 ปี กลับมาฉันไม่อยากเชื่อศาสนาที่ปลอมตัวเป็นวิทยาศาสตร์ที่คุณลุยเมื่อคุณพักผ่อนใน google + เชื่อถือได้ ความสับสนเป็นตำนาน

ฉันจะแบ่งคำถามกว้าง ๆ นี้ออกเป็นสามข้อ:

  • บริการดาวน์สตรีม บริการเว็บใด ๆ ที่คุณพัฒนาจะมีบริการดาวน์สตรีมที่คุณใช้และมีธุรกรรมไวยากรณ์ที่คุณไม่มีทางเลือกนอกจากทำตาม คุณควรลองซ่อนทั้งหมดนี้จากผู้ใช้บริการของคุณและตรวจสอบให้แน่ใจว่าทุกส่วนของการดำเนินการของคุณประสบความสำเร็จหรือล้มเหลวในฐานะกลุ่มจากนั้นส่งคืนผลลัพธ์นี้ให้ผู้ใช้ของคุณ
  • บริการของคุณ ลูกค้าต้องการผลลัพธ์ที่ชัดเจนสำหรับการเรียกใช้บริการเว็บและรูปแบบ REST ตามปกติในการทำคำขอ POST, PUT หรือ DELETE โดยตรงจากแหล่งข้อมูลสำคัญทำให้ฉันเป็นคนจนและได้รับการปรับปรุงอย่างง่ายดาย หากคุณใส่ใจความน่าเชื่อถือคุณต้องระบุคำขอการดำเนินการ ID นี้สามารถเป็น guid ที่สร้างขึ้นบนไคลเอนต์หรือค่าเมล็ดจากฐานข้อมูลเชิงสัมพันธ์บนเซิร์ฟเวอร์มันไม่สำคัญ สำหรับ ID ของเซิร์ฟเวอร์ที่สร้างขึ้นให้ใช้การตอบสนองคำขอ 'preflight' เพื่อแลกเปลี่ยน ID ของการดำเนินการ หากการร้องขอนี้ล้มเหลวหรือครึ่งหนึ่งประสบความสำเร็จไม่มีปัญหาลูกค้าเพียงแค่ทำซ้ำการร้องขอ รหัสที่ไม่ได้ใช้จะไม่เป็นอันตราย

    สิ่งนี้มีความสำคัญเพราะช่วยให้คำขอที่ตามมาทั้งหมดเป็น idempotent ได้อย่างเต็มที่ในแง่ที่ว่าหากพวกเขาทำซ้ำ n ครั้งพวกเขาจะได้ผลลัพธ์เดียวกันและทำให้ไม่มีอะไรเกิดขึ้นอีก เซิร์ฟเวอร์จัดเก็บการตอบสนองทั้งหมดกับรหัสการกระทำและถ้าเห็นคำขอเดียวกันมันจะตอบสนองการตอบสนองเดียวกัน การรักษาแบบเต็มรูปแบบอยู่ในgoogle docนี้ เอกสารแนะนำการใช้งานที่ฉันเชื่อว่า (!) ตามหลักการของ REST ในวงกว้าง ผู้เชี่ยวชาญจะบอกฉันว่ามันละเมิดผู้อื่นอย่างไร รูปแบบนี้สามารถใช้ประโยชน์ได้สำหรับการโทรที่ไม่ปลอดภัยไปยังบริการเว็บของคุณไม่ว่าจะมีการทำธุรกรรมดาวน์สตรีมหรือไม่ก็ตาม
  • การรวมบริการของคุณเข้ากับ "ธุรกรรม" ควบคุมโดยบริการอัปสตรีม ในบริบทของเว็บบริการการทำธุรกรรมกรดเต็มรูปแบบจะถือว่าเป็นมักจะไม่คุ้มค่าความพยายาม แต่คุณสามารถช่วยให้ผู้บริโภคอย่างมากในการให้บริการของคุณโดยให้ยกเลิกและ / หรือการเชื่อมโยงยืนยันในการตอบสนองการยืนยันของคุณและทำให้บรรลุการทำธุรกรรมโดยการชดเชย

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


9

คุณจะต้องม้วนประเภทการจัดการเท็กซัส "รหัสประจำตัว" ของคุณเอง ดังนั้นมันจะเป็นการโทร 4 สาย:

http://service/transaction (some sort of tx request)
http://service/bankaccount/bob (give tx id)
http://service/bankaccount/john (give tx id)
http://service/transaction (request to commit)

คุณจะต้องจัดการกับการจัดเก็บการดำเนินการในฐานข้อมูล (หากโหลดสมดุล) หรือในหน่วยความจำหรือจากนั้นจัดการการกระทำย้อนกลับหมดเวลา

ไม่ใช่วันพักผ่อนในสวนสาธารณะจริงๆ


4
ฉันไม่คิดว่านี่เป็นตัวอย่างที่ดีโดยเฉพาะ คุณต้องการเพียงสองขั้นตอน: สร้างธุรกรรม (สร้างธุรกรรมในสถานะ "รอดำเนินการ") และธุรกรรมยอมรับ (กระทำหากไม่มีข้อผูกมัดและย้ายทรัพยากรไปยังสถานะที่กำหนดหรือย้อนกลับ)
Jon Watte

2

ฉันคิดว่าในกรณีนี้มันเป็นที่ยอมรับโดยสิ้นเชิงที่จะทำลายทฤษฎีบริสุทธิ์ของ REST ในสถานการณ์นี้ ไม่ว่าในกรณีใดฉันไม่คิดว่าจะมีอะไรใน REST ที่บอกว่าคุณไม่สามารถสัมผัสวัตถุที่ต้องพึ่งพาในกรณีธุรกิจที่ต้องใช้

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


2

ประการแรกการโอนเงินคืออะไรที่คุณไม่สามารถทำได้ในการเรียกใช้ทรัพยากรเดียว การกระทำที่คุณต้องการจะส่งเงิน ดังนั้นคุณเพิ่มทรัพยากรการโอนเงินไปยังบัญชีของผู้ส่ง

POST: accounts/alice, new Transfer {target:"BOB", abmount:100, currency:"CHF"}.

เสร็จสิ้น คุณไม่จำเป็นต้องรู้ว่านี่เป็นธุรกรรมที่ต้องเป็นปรมาณูเป็นต้นคุณเพิ่งโอนเงินหรือที่รู้จัก ส่งเงินจาก A ถึง B


แต่สำหรับกรณีที่หายากนี่เป็นคำตอบทั่วไป:

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

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

วิธีนี้สามารถส่งผ่านสถานะภายในไปรอบ ๆ และใช้การเข้ารหัสและการเซ็นชื่อระบบสามารถยังคงปลอดภัยและเสียง การค้นหาสิ่งที่เป็นนามธรรมที่ถูกต้องสำหรับลูกค้าว่าทำไมเขาถึงส่งข้อมูลรัฐให้เป็นเรื่องที่ขึ้นอยู่กับการออกแบบและสถาปัตยกรรม


ทางออกที่แท้จริง:

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

จำสิ่งที่เขียนใน Wikipedia เกี่ยวกับคุกกี้ HTTP:

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

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


ทางออกที่ดีกว่า:

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

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

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

ตัวอย่างที่ซับซ้อน:

ซื้อบ้าน:

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

ขั้นตอนเหล่านี้อาจใช้เวลาหลายวันกว่าจะเสร็จสมบูรณ์บางขั้นตอนสามารถทำขนานกันได้เป็นต้น

ในการทำเช่นนี้คุณเพียงแค่ให้ตัวแทนงานซื้อบ้านเช่น:

POST: agency.com/ { task: "buy house", target:"link:toHouse", credibilities:"IamMe"}.

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

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


1

คุณต้องไม่ใช้ธุรกรรมด้านเซิร์ฟเวอร์ใน REST

หนึ่งในข้อ จำกัด ของ REST:

ไร้สัญชาติ

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

วิธีที่สงบเพียงอย่างเดียวคือการสร้างธุรกรรมการทำซ้ำบันทึกและใส่ลงในสถานะของลูกค้า ด้วยการร้องขอของลูกค้าส่งบันทึกการทำซ้ำและเซิร์ฟเวอร์ทำธุรกรรมและ

  1. ย้อนกลับการทำธุรกรรม แต่ให้บันทึกการทำซ้ำการทำธุรกรรมใหม่ (อีกหนึ่งขั้นตอน)
  2. หรือในที่สุดก็ทำธุรกรรมให้เสร็จ

แต่อาจจะง่ายกว่าถ้าใช้เทคโนโลยีที่อิงกับเซสชันของเซิร์ฟเวอร์ที่รองรับธุรกรรมด้านเซิร์ฟเวอร์


ข้อความอ้างอิงมาจากรายการ Rik wikipedia นี่เป็นแหล่งที่มาจริงหรือวิกิพีเดียได้มาจากที่อื่นหรือไม่ ใครจะพูดว่าบริบทลูกค้าคืออะไรและบริบทเซิร์ฟเวอร์คืออะไร
bbsimonbb

1

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

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

ไม่ทราบสถานการณ์ที่ซับซ้อนเช่นการจองตั๋วเครื่องบินหลายสายหรือสถาปัตยกรรมขนาดเล็ก

ผมพบว่ากระดาษเกี่ยวกับเรื่องที่เกี่ยวข้องกับประสบการณ์ของการจัดการกับ atomicity การทำธุรกรรมในการให้บริการสงบ


0

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

ดังนั้นเพื่อให้การถ่ายโอนระหว่าง<url-base>/account/aและคุณสามารถโพสต์ต่อไปนี้<url-base>/account/b<url-base>/transfer

<การโอน>
    <จาก> <url ฐาน> / บัญชี / a </ จาก>
    <ที่> <url ฐาน> / บัญชี / b </ ที่>
    <จำนวนเงิน> 50 </ จำนวน>
</ ถ่ายโอน>

นี้จะสร้างทรัพยากรการถ่ายโอนใหม่และกลับ URL ใหม่ของการถ่ายโอน - <url-base>/transfer/256ตัวอย่างเช่น

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

อย่างไรก็ตามสิ่งนี้ไม่ครอบคลุมการทำธุรกรรมแบบกระจาย (ถ้าพูดว่า 'a' ถูกจัดขึ้นที่ธนาคารหนึ่งหลังบริการหนึ่งและ 'b' จัดขึ้นที่ธนาคารอื่นที่อยู่หลังบริการอื่น) - นอกเหนือจากที่จะพูดว่า "ลองใช้วลีทั้งหมด การดำเนินงานในรูปแบบที่ไม่ต้องการการทำธุรกรรมแบบกระจาย "


2
หากคุณไม่สามารถ "ดำเนินการทั้งหมดในรูปแบบที่ไม่ต้องการธุรกรรมแบบกระจาย" แสดงว่าคุณต้องใช้การดำเนินการสองขั้นตอนจริงๆ ความคิดที่ดีที่สุดที่ฉันสามารถหาได้สำหรับการใช้งานสองเฟสบน REST คือrest.blueoxen.net/cgi-bin/wiki.pl?TwoPhaseCommitซึ่งที่สำคัญไม่รบกวน URL เนมสเปซ ทำความสะอาดความหมายส่วนที่เหลือ
Phasmal

3
ปัญหาอื่น ๆ ที่มีข้อเสนอแนะนี้คือถ้า hiccups แคชและ POSTs สองครั้งคุณจะได้รับการถ่ายโอนสองครั้ง
Jon Watte

จริงในกรณีนี้คุณต้องมีกระบวนการสองขั้นตอน - สร้างทรัพยากร "ถ่ายโอน" ด้วย URL ที่ไม่ซ้ำกันจากนั้นเพิ่มรายละเอียดการถ่ายโอนไปเป็นส่วนหนึ่งของการส่งมอบ (สองส่วนดังกล่าวในคำตอบอื่น ๆ ) แน่นอนว่านี่อาจเป็นวลีที่สร้างทรัพยากร "ธุรกรรม" จากนั้นเพิ่มการดำเนินการ "โอนย้าย" ลงไป
Phasmal

-3

ฉันเดาว่าคุณสามารถใส่ TAN ใน URL / ทรัพยากรได้:

  1. ใส่ / ทำธุรกรรมเพื่อรับ ID (เช่น "1")
  2. [PUT, GET, POST, อะไรก็ตาม] / 1 / บัญชี / bob
  3. [ใส่, รับ, โพสต์, อะไรก็ตาม] / 1 / บัญชี / ใบเรียกเก็บเงิน
  4. ลบ / ทำธุรกรรมด้วย ID 1

แค่ความคิด


ฉันเห็นปัญหาสองข้อด้วยวิธีนี้: 1) มันบอกเป็นนัยว่าคุณไม่สามารถเข้าถึงทรัพยากรนอกการทำธุรกรรม (แม้ว่าอาจจะไม่ใช่เรื่องใหญ่) 2) ไม่มีคำตอบใด ๆ ที่สัมผัสได้ถึงความจริงที่ว่าเซิร์ฟเวอร์นั้นไม่มีตัวตนอีกต่อไปแม้ว่าฉันจะสงสัยว่าไม่มีอะไรสามารถทำได้
Gili

ทีนี้ / 1 / account / bob และ / account / bob เป็นเพียงสองแหล่งข้อมูลที่แตกต่างกัน :) และ RE: ไร้สัญชาติก็หมายความว่าทรัพยากรพร้อมใช้งานเสมอและไม่ขึ้นอยู่กับคำขอก่อนหน้า เนื่องจากคุณขอธุรกรรมใช่ว่าไม่ใช่กรณี แต่คุณต้องการธุรกรรมอีกครั้ง
จนถึง

1
หากลูกค้าต้องรวบรวม URIs API ของคุณจะไม่สงบ
aehlke

1
ฉันไม่เข้าใจพวกคุณจริงๆ! หากคุณถือว่าธุรกรรมเป็นทรัพยากร (เช่นในตัวอย่างด้านบน) คุณเพียงแค่หยุดการทำธุรกรรมในแง่ความคลาสสิคและใช้มันใน "วิธี REST ที่เหมาะสม" ซึ่งจะช่วยลดความยุ่งยากในการเขียนโปรแกรมกระบวนการทำธุรกรรม ตัวอย่างเช่นคุณสามารถรวม href ไปยังธุรกรรมในการตอบสนองของคุณเพื่อหลีกเลี่ยงการกระจัดในสภาพแวดล้อมฝั่งเซิร์ฟเวอร์แบบกระจายก็ยังไร้สัญชาติ (เป็นเพียงทรัพยากรใช่หรือไม่) และคุณสามารถใช้กลไกธุรกรรมจริงได้ ต้องการ (ถ้าคุณไม่มี DB ที่ด้านหลัง?)
Matthias Hryniszak

1
ไม่ทางใดก็ทางหนึ่งถ้าคุณหยุดคิด SQL / SOAP แล้วเริ่มคิด HTTP (เหมือนที่เบราว์เซอร์ทำ) ทุกอย่างกลายเป็นเรื่องง่าย
Matthias Hryniszak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.