REST คอมเพล็กซ์ / ทรัพยากรรวม / ซ้อนกัน [ปิด]


177

ฉันพยายามล้อมรอบวิธีที่ดีที่สุดในการจัดการกับแนวคิดใน API ที่ใช้ REST แหล่งข้อมูลที่ไม่มีทรัพยากรอื่น ๆ ไม่มีปัญหา ที่ที่ฉันประสบปัญหาคือทรัพยากรที่ซับซ้อน

ตัวอย่างเช่นฉันมีทรัพยากรสำหรับหนังสือการ์ตูน ComicBookมีทุกประเภทของคุณสมบัติที่มันชอบauthor, issue number, dateฯลฯ

หนังสือการ์ตูนยังมีรายการ1..nปก สิ่งเหล่านี้เป็นวัตถุที่ซับซ้อน พวกเขามีข้อมูลมากมายเกี่ยวกับหน้าปก: ศิลปินวันที่และแม้แต่ภาพที่เข้ารหัสพื้นฐาน 64 ของหน้าปก

สำหรับGETวันที่ComicBookฉันสามารถกลับการ์ตูนและครอบคลุมทั้งหมดรวมถึงภาพฐานของพวกเขา 64 นั่นอาจไม่ใช่เรื่องใหญ่สำหรับการสร้างการ์ตูนเรื่องเดียว แต่สมมติว่าฉันกำลังสร้างแอปไคลเอ็นต์ที่ต้องการแสดงรายการการ์ตูนทั้งหมดในระบบในตาราง
ตารางจะมีคุณสมบัติบางอย่างจากComicBookทรัพยากร แต่แน่นอนว่าเราจะไม่ต้องการแสดงหน้าปกทั้งหมดในตาราง การส่งคืนหนังสือการ์ตูน 1,000 เล่มแต่ละเล่มที่มีหน้าปกหลายปกจะส่งผลให้ข้อมูลจำนวนมหาศาลหลั่งไหลเข้ามาในสายข้อมูลที่ไม่จำเป็นสำหรับผู้ใช้ปลายทางในกรณีนี้

สัญชาตญาณของฉันคือการสร้างCoverทรัพยากรและมีComicBookหน้าปก ดังนั้นตอนนี้Coverคือ URI GETในหนังสือการ์ตูนทำงานได้ตอนนี้แทนที่จะเป็นCoverแหล่งทรัพยากรขนาดใหญ่ที่เราส่ง URI กลับมาสำหรับแต่ละหน้าปกและลูกค้าสามารถดึงข้อมูลหน้าปกได้ตามต้องการ

ตอนนี้ฉันมีปัญหากับการสร้างการ์ตูนใหม่ แน่นอนฉันจะต้องการสร้างอย่างน้อยหนึ่งครอบคลุมเมื่อฉันสร้างComicในความเป็นจริงที่อาจเป็นกฎธุรกิจ
ดังนั้นตอนนี้ผมติดผมทั้งบังคับให้ลูกค้าในการบังคับใช้กฎเกณฑ์ทางธุรกิจครั้งแรกโดยส่งCover, รับ URI สำหรับปกที่แล้วPOSTไอเอ็นจีComicBookกับ URI ในรายการหรือของฉันPOSTในการComicBookใช้เวลาในทรัพยากรมองที่แตกต่างกันเกินกว่าที่จะถ่มน้ำลาย ออก. ทรัพยากรที่เข้ามาสำหรับPOSTและGETเป็นสำเนาลึกโดยที่GETs ขาออกประกอบด้วยการอ้างอิงไปยังทรัพยากรที่ต้องพึ่งพา

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


การใช้RESTFUL SERVICE DISCOVERYสมเหตุสมผลหรือไม่
treecoder

1
ฉันพยายามที่จะปฏิบัติตาม HATEAOS ซึ่งในความคิดของฉันจะตอบโต้การใช้บางอย่างเช่นนั้น แต่ฉันจะดู
jgerman

คำถามที่แตกต่างในจิตวิญญาณเดียวกัน อย่างไรก็ตามความเป็นเจ้าของนั้นแตกต่างจากโซลูชันที่คุณเสนอ (หนึ่งในคำถาม) stackoverflow.com/questions/20951419/…
Wes

คำตอบ:


64

@ray การสนทนาที่ยอดเยี่ยม

@ jgerman อย่าลืมว่าเพียงเพราะ REST ไม่ได้หมายความว่าจะต้องใช้ทรัพยากรจาก POST

สิ่งที่คุณเลือกที่จะรวมไว้ในการนำเสนอทรัพยากรใด ๆ นั้นขึ้นอยู่กับคุณ

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

กรณีเฉพาะของคุณนั้นมีความซับซ้อนมากขึ้นเล็กน้อยซึ่งปกหนังสือนั้นต้องการหนังสือการ์ตูนจริง ๆ และวีซ่ากลับกัน

อย่างไรก็ตามหากคุณพิจารณาข้อความอีเมลเป็นแหล่งข้อมูลและที่อยู่จากเป็นทรัพยากรย่อยคุณจะยังคงสามารถอ้างอิงจากที่อยู่แยกต่างหากได้ ตัวอย่างเช่นรับทั้งหมดจากที่อยู่ หรือสร้างข้อความใหม่ด้วยที่อยู่ก่อนหน้าจากที่อยู่ หากอีเมลเป็น REST คุณจะเห็นได้อย่างง่ายดายว่าทรัพยากรที่มีการอ้างอิงโยงหลายตัวสามารถใช้งานได้ / / ได้รับข้อความ, / ข้อความร่าง, / จากที่อยู่ / ถึงที่อยู่ / ที่อยู่ / อาสาสมัคร / สิ่งที่แนบมา / โฟลเดอร์ , / tags, / categories, / label, et al.

บทช่วยสอนนี้แสดงตัวอย่างที่ดีของแหล่งข้อมูลอ้างอิงข้าม http://www.peej.co.uk/articles/restfully-delicious.html

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

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

แบบฟอร์ม & พารามิเตอร์แตกต่างจากการเป็นตัวแทนของทรัพยากร HTML แล้ว การโพสต์พารามิเตอร์ไบนารี / ไฟล์ซึ่งทำให้ URL ไม่ยืด

เมื่อคุณได้รับแบบฟอร์มสำหรับทรัพยากรใหม่ (/ comic-books / new) หรือรับแบบฟอร์มเพื่อแก้ไขทรัพยากร (/ comic-books / 0 / แก้ไข) คุณกำลังขอการแทนเฉพาะของแบบฟอร์มของทรัพยากร หากคุณโพสต์ลงในคอลเล็กชันทรัพยากรด้วยแอปพลิเคชัน / x-www-form-urlencoded "เนื้อหา" หรือ "multipart / form-data" คุณจะขอให้เซิร์ฟเวอร์บันทึกการแสดงประเภทนั้น เซิร์ฟเวอร์สามารถตอบสนองด้วยการเป็นตัวแทน HTML ซึ่งถูกบันทึกไว้หรืออะไรก็ตาม

คุณอาจต้องการอนุญาตให้มีการโพสต์ HTML, XML หรือ JSON ลงในการรวบรวมทรัพยากรเพื่อวัตถุประสงค์ของ API หรือคล้ายกัน

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

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

GET / comic-books
=> 200 ตกลง, รับหนังสือการ์ตูนทั้งหมด

GET / หนังสือการ์ตูน / 0
=> 200 ตกลง, รับหนังสือการ์ตูน (id: 0) พร้อมหน้าปก (/ หน้าปก / 1, / หน้าปก / 2)

GET / หนังสือการ์ตูน / 0 / หน้าปก
=> 200 ตกลง, รับความคุ้มครองสำหรับหนังสือการ์ตูน (id: 0)

GET / covers
=> 200 ตกลงรับความคุ้มครองทั้งหมด

GET / covers / 1
=> 200 ตกลง, รับ cover (id: 1) พร้อมหนังสือการ์ตูน (/ comic-books / 0)

GET / หนังสือการ์ตูน / ใหม่
=> 200 ตกลง, รับแบบฟอร์มเพื่อสร้างหนังสือการ์ตูน (แบบฟอร์ม: POST / ฉบับร่างหนังสือการ์ตูน)

POST / ฉบับร่างหนังสือการ์ตูน
=
ผู้แต่งfoo =
ผู้จัดพิมพ์boo = goo
เผยแพร่ = 2011-01-01
=> 302 พบ, ที่ตั้ง: / ฉบับร่างหนังสือการ์ตูน / 3, เปลี่ยนเส้นทางไปยังหนังสือการ์ตูนฉบับร่าง (id: 3) ด้วย ครอบคลุม (ไบนารี)

GET / draft-comic-books / 3
=> 200 ตกลง, รับร่างหนังสือการ์ตูน (id: 3) พร้อมปก

GET / ฉบับร่างหนังสือ / 3 / หน้าปก
=> 200 ตกลงรับความคุ้มครองสำหรับหนังสือการ์ตูนฉบับร่าง (/ ฉบับร่างหนังสือการ์ตูน / 3)

GET / ร่างหนังสือการ์ตูน / 3 / หน้าปก / ใหม่
=> 200 ตกลง, รับแบบฟอร์มเพื่อสร้างหน้าปกสำหรับหนังสือการ์ตูนฉบับร่าง (/ ฉบับร่างหนังสือการ์ตูน / 3) (แบบฟอร์ม: POST / ร่างหนังสือการ์ตูน / 3 / ปก).

POST / draft-comic-books / 3 / cover
cover_type = front
cover_data = (ไบนารี)
=> 302 พบ, ที่อยู่: / ฉบับร่าง / หนังสือการ์ตูน / 3 / ปก, เปลี่ยนเส้นทางไปยังหน้าปกใหม่สำหรับหนังสือการ์ตูนร่าง (/ ร่างการ์ตูน -book / 3 / ปก / 1)

GET / ร่างหนังสือการ์ตูน / 3 / เผยแพร่
=> 200 ตกลง, รับแบบฟอร์มเพื่อเผยแพร่หนังสือการ์ตูนฉบับร่าง (id: 3) (แบบฟอร์ม: POST / หนังสือการ์ตูนที่ตีพิมพ์)

POST / หนังสือการ์ตูนที่ตีพิมพ์
=
ผู้แต่งfoo =
ผู้เผยแพร่boo = ผู้เผยแพร่ goo
= 2011-01-01
cover_type = cover_data ด้านหน้า
= (ไบนารี)
=> 302 พบ, ที่อยู่: / หนังสือการ์ตูน / 3, เปลี่ยนเส้นทางไปเป็นหนังสือการ์ตูนที่ตีพิมพ์ (id: 3) พร้อมผ้าคลุม


ฉันเป็นมือใหม่ทั้งหมดในเรื่องนี้และพยายามเรียนรู้อย่างเร่งด่วน ฉันพบว่าสิ่งนี้มีประโยชน์มาก อย่างไรก็ตามในบล็อกอื่น ๆ เป็นต้นฉันได้อ่านมาวันนี้การใช้ GET เพื่อดำเนินการ (โดยเฉพาะอย่างยิ่งการดำเนินการที่ไม่ใช้ idempotent) ดังนั้นไม่ควรเป็น POST / ฉบับร่างหนังสือการ์ตูน / 3 / เผยแพร่?
Gary McGill

3
@GaryMcGill ในตัวอย่างของเขา / draft-comic-books / 3 / Publishing จะส่งคืนแบบฟอร์ม HTML เท่านั้น (ไม่ได้แก้ไขข้อมูลใด ๆ )
Olivier Lalonde

@Olivier ถูกต้อง คำที่เผยแพร่อยู่ที่นั่นเพื่อแสดงว่าแบบฟอร์มทำอะไร อย่างไรก็ตามเนื่องจากคุณต้องการเก็บคำกริยาไว้ในวิธี HTTP คุณควรโพสต์ไปยังแหล่งข้อมูลสำหรับหนังสือการ์ตูนที่ตีพิมพ์ ... หากนี่เป็นเว็บไซต์คุณอาจต้องใช้ URI เพื่อฟอร์มเผยแพร่บางสิ่ง ... แม้ว่าหากการดำเนินการเผยแพร่เป็นเพียงปุ่มเดียวในหน้าหนังสือการ์ตูนแบบฟอร์มปุ่มเดียวสามารถโพสต์โดยตรงไปยัง / URI ที่ตีพิมพ์ / หนังสือการ์ตูน
Alex

@Alex ในคำขอ POST ฉันจะส่งคืน 201 สร้างโดยมี URL ของทรัพยากรใหม่เป็นตำแหน่งในส่วนหัวการตอบกลับ
ismriv

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

45

การรักษาความลับเนื่องจากทรัพยากรเป็นหัวใจของ REST โดยเฉพาะ HATEOAS ดังนั้นใช่GETคำขอhttp://example.com/comic-books/1จะให้คุณเป็นตัวแทนของหนังสือเล่มที่ 1 พร้อมคุณสมบัติรวมถึงชุดของ URIs สำหรับการครอบคลุม จนถึงตอนนี้ดีมาก

คำถามของคุณคือวิธีจัดการกับการสร้างหนังสือการ์ตูน หากกฎธุรกิจของคุณคือหนังสือจะมีหน้าปก0 หรือมากกว่าคุณก็ไม่มีปัญหา:

POST http://example.com/comic-books

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

POST http://example.com/comic-books/8/covers

ด้วยฝาครอบในร่างกายนิติบุคคล

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

  1. บังคับให้มีการสร้างปกก่อนตอนนี้โดยพื้นฐานแล้วเป็นการสร้างทรัพยากรที่ไม่ขึ้นอยู่กับหรือคุณวางปกเริ่มต้นในเนื้อหาของ POST ที่สร้างหนังสือการ์ตูน สิ่งนี้ตามที่คุณพูดหมายความว่าการเป็นตัวแทนที่คุณโพสต์เพื่อสร้างจะแตกต่างจากการเป็นตัวแทนที่คุณได้รับ

  2. กำหนดความคิดของปกหลักหรือเริ่มต้นหรือที่ต้องการหรือที่กำหนดไว้เป็นอย่างอื่น นี่น่าจะเป็นการแฮ็คโมเดลและถ้าคุณทำแบบนั้นมันก็เหมือนกับการปรับแต่งโมเดลวัตถุของคุณ (โมเดลแนวคิดหรือโมเดลธุรกิจ) เพื่อให้พอดีกับเทคโนโลยี ไม่ใช่ความคิดที่ดี

คุณควรชั่งน้ำหนักสองตัวเลือกเหล่านี้เพื่อให้ง่ายขึ้น

คุณควรเลือกทางใดในสามข้อนี้ ไม่ทราบเกี่ยวกับสถานการณ์ของคุณมากเกินไป แต่ตอบคำถามทั่วไปเกี่ยวกับทรัพยากร 1..N ฉันจะพูดว่า:

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

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

  • หากคุณต้องการ 1..N และการคุ้มครองยังคงพึ่งพาเพียงแค่ผ่อนคลายสัญชาตญาณของคุณเพื่อให้การรับรองใน POST และ GET เหมือนกันหรือทำให้เป็นแบบเดียวกัน

รายการสุดท้ายมีการอธิบายดังนี้:

<comic-book>
  <name>...</name>
  <edition>...</edition>
  <cover-image>...BASE64...</cover-image>
  <cover-image>...BASE64...</cover-image>
  <cover>...URI...</cover>
  <cover>...URI...</cover>
</comic-book>

เมื่อคุณโพสต์คุณอนุญาตให้มีปัสสาวะถ้าคุณมีพวกเขา (ยืมมาจากหนังสือเล่มอื่น ๆ ) แต่ยังใส่ในภาพเริ่มต้นหนึ่งหรือมากกว่า หากคุณกำลังสร้างหนังสือและเอนทิตีของคุณไม่มีภาพหน้าปกเริ่มต้นส่งคืน 409 หรือการตอบกลับที่คล้ายกัน ใน GET คุณสามารถส่งคืน URI ได้

ดังนั้นโดยพื้นฐานแล้วคุณอนุญาตให้ตัวแทน POST และ GET เป็น "เหมือนกัน" แต่คุณเลือกที่จะไม่ "ใช้" ภาพหน้าปกบน GET หรือปกบน POST หวังว่ามันสมเหตุสมผล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.