การออกแบบ REST api โดย URI เทียบกับสตริงแบบสอบถาม


73

สมมติว่าฉันมีสามทรัพยากรที่เกี่ยวข้องดังนี้:

Grandparent (collection) -> Parent (collection) -> and Child (collection)

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

หากลูกค้าของฉันส่งรหัสอ้างอิงถึงปู่ย่าตายายให้ฉันฉันต้องการค้นหาเฉพาะเด็กที่เป็นทายาทสายตรงของปู่ย่าตายายนั้นเท่านั้น

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

ฉันคิดถึงบางสิ่งเช่นนั้น:

GET /myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text}

และ

GET /myservice/api/v1/parents/{parentID}/children?search={text}

สำหรับข้อกำหนดข้างต้นตามลำดับ

แต่ฉันสามารถทำอะไรเช่นนี้:

GET /myservice/api/v1/children?search={text}&grandparentID={id}&parentID=${id}

ในการออกแบบนี้ฉันสามารถอนุญาตให้ลูกค้าของฉันส่งต่อฉันอย่างใดอย่างหนึ่งในสตริงการสืบค้น: grandparentID หรือ parentID แต่ไม่ใช่ทั้งสองอย่าง

คำถามของฉันคือ:

1) การออกแบบ API ใดที่สงบมากขึ้นและทำไม ความหมายพวกมันหมายถึงและประพฤติตนในลักษณะเดียวกัน ทรัพยากรสุดท้ายใน URI คือ "children" หมายความว่าไคลเอ็นต์กำลังทำงานบนทรัพยากรชายด์อย่างมีประสิทธิภาพ

2) อะไรคือข้อดีข้อเสียของแต่ละคนในแง่ของความเข้าใจในมุมมองของลูกค้าและการบำรุงรักษาจากมุมมองของนักออกแบบ

3) สตริงการสืบค้นที่ใช้จริงๆนอกเหนือจาก "การกรอง" ในทรัพยากรของคุณคืออะไร หากคุณใช้วิธีแรกพารามิเตอร์ตัวกรองจะถูกฝังใน URI เองเป็นพารามิเตอร์พา ธ แทนที่จะเป็นพารามิเตอร์สตริงข้อความค้นหา

ขอบคุณ!


3
ชื่อคำถามของคุณน่าสับสนอย่างยิ่งกับทุกคนที่ดูสิ่งนี้ เซ็กเมนต์ที่ถูกต้องของ URI นั้นถูกกำหนดเป็น <scheme>: // <user>: <password> @ <host>: <port> / <path>; <params>? <quams>? <query> / # <fragment> (แม้ว่า < รหัสผ่าน> เลิกใช้แล้ว) "สตริงข้อความค้นหา" เป็นส่วนประกอบที่ถูกต้องของ URI ดังนั้น "vs" ของคุณในชื่อเรื่องคือการพูดคุยอย่างบ้าคลั่ง
เค. อลันเบตส์

คุณหมายถึงI want to only search against children who are INdirect descendants of that grandparent.อะไร ตามโครงสร้างของคุณปู่ย่าตายายไม่มีลูกโดยตรง
null

ความแตกต่างระหว่างเด็กกับผู้ปกครองคืออะไร? พ่อแม่เป็นพ่อแม่หรือไม่ถ้าเขาไม่มีลูก? กลิ่นของความผิดพลาดในการออกแบบ
Pinoniq

เรื่อง: potential design flawและถ้าคุณมีข้อมูลเกี่ยวกับบุคคล แต่ไม่มีข้อมูลเกี่ยวกับผู้ปกครองพวกเขามีคุณสมบัติเป็นchild? (เช่น Adam และ Eve) :)
Jesse Chisholm

คำตอบ:


64

เป็นครั้งแรก

ตามRFC 3986 §3.4 (Uniform Resource Identifiers § (ส่วนประกอบไวยากรณ์) | Query

3.4 การค้นหา

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

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

REST ไม่เกี่ยวข้องกับคำจำกัดความนี้

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

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

?first_name={firstName}&last_name={lastName}&birth_date={birthDate} เป็นต้น

เพื่อตอบคำถามเฉพาะของคุณ

1) การออกแบบ API ใดที่สงบมากขึ้นและทำไม ความหมายพวกมันหมายถึงและประพฤติตนในลักษณะเดียวกัน ทรัพยากรสุดท้ายใน URI คือ "children" หมายความว่าไคลเอ็นต์กำลังทำงานบนทรัพยากรชายด์อย่างมีประสิทธิภาพ

ฉันไม่คิดว่านี่จะเป็นเรื่องที่ชัดเจนอย่างที่คุณเชื่อ

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

สิ่งที่วรรณกรรม REST กล่าวคือถ้าคุณจะใช้ HTTP เป็นโปรโตคอลแอปพลิเคชันของคุณคุณจะต้องปฏิบัติตามข้อกำหนดอย่างเป็นทางการของข้อกำหนดของโปรโตคอลและคุณไม่สามารถ "สร้าง http ขึ้นมาในขณะที่คุณไปและยังประกาศว่าคุณกำลังใช้ http" ; หากคุณจะใช้ URIs ในการระบุทรัพยากรของคุณคุณจะต้องปฏิบัติตามข้อกำหนดอย่างเป็นทางการของข้อกำหนดที่เกี่ยวข้องกับ URI / URL

คำถามของคุณได้รับการแก้ไขโดยตรงจาก RFC3986 §3.4ซึ่งฉันได้ทำการเชื่อมโยงไปแล้ว บรรทัดล่างของเรื่องนี้คือแม้ว่า URI ที่สอดคล้องจะไม่เพียงพอที่จะพิจารณา API "RESTful" หากคุณต้องการให้ระบบของคุณเป็น "RESTful" และคุณใช้ HTTP และ URIs คุณจะไม่สามารถระบุข้อมูลลำดับชั้นผ่าน สตริงข้อความค้นหาเนื่องจาก:

3.4 การค้นหา

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

... มันง่ายอย่างนั้น

2) อะไรคือข้อดีข้อเสียของแต่ละคนในแง่ของความเข้าใจในมุมมองของลูกค้าและการบำรุงรักษาจากมุมมองของนักออกแบบ

"ข้อดี" ของสองครั้งแรกที่พวกเขาจะอยู่บนเส้นทางที่ถูกต้อง "ข้อเสีย" ของอันที่สามคือมันดูเหมือนจะผิดปกติ

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

การแปล URIs ที่เป็นคำต่อคำของคุณไปสู่ความหมายเชิงความหมายของพวกเขา /myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text} สิ่งนี้พูดถึงผู้ปกครองของปู่ย่าตายายให้ค้นหาลูกของพวกเขาที่มีsearch={text} สิ่งที่คุณพูดกับ URI ของคุณนั้นจะติดต่อกันเฉพาะเมื่อค้นหาพี่น้อง ด้วย "ปู่ย่าตายาย, ผู้ปกครอง, เด็ก ๆ " คุณพบว่า "ปู่ย่าตายาย" ขึ้นรุ่นหนึ่งไปยังพ่อแม่ของพวกเขาแล้วกลับลงมาสู่รุ่น "ปู่ย่าตายาย" โดยดูที่ลูก ๆ ของผู้ปกครอง

/myservice/api/v1/parents/{parentID}/children?search={text} สิ่งนี้บอกว่าสำหรับผู้ปกครองที่ระบุโดย {parentID} ให้ค้นหาลูกของพวกเขาที่มี?search={text}สิ่งนี้อยู่ใกล้เพื่อแก้ไขสิ่งที่คุณต้องการและแสดงถึงความสัมพันธ์ของผู้ปกครอง -> เด็กที่น่าจะถูกนำมาใช้เพื่อจำลอง API ทั้งหมดของคุณ ในการสร้างแบบจำลองด้วยวิธีนี้ภาระจะถูกวางไว้บนไคลเอนต์เพื่อรับรู้ว่าถ้าพวกเขามี "grandparentId" ว่ามีเลเยอร์ของการเปลี่ยนทิศทางระหว่าง ID ที่พวกเขามีและส่วนของกราฟครอบครัวที่พวกเขาต้องการเห็น หากต้องการค้นหา "child" โดย "grandparentId" คุณสามารถโทรหา/parents/{parentID}/childrenบริการของคุณและจากนั้น foreach child ที่ส่งคืนแล้วค้นหาลูก ๆ ของพวกเขาเพื่อหาตัวบ่งชี้บุคคลของคุณ

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

1)อันแรกฉันได้พาดพิงถึง แสดงกราฟของ "คน" เป็นโครงสร้างคอมโพสิต แต่ละคนมีการอ้างอิงถึงรุ่นที่อยู่เหนือเส้นทางของผู้ปกครองและรุ่นที่อยู่ด้านล่างนั้นผ่านเส้นทางลูกของตน

/Persons/Joe/Parents/Mother/Parents จะเป็นวิธีที่จะคว้าปู่ย่าตายายของโจ

/Persons/Joe/Parents/Parents จะเป็นวิธีที่จะคว้าปู่ย่าตายายทั้งหมดของโจ

/Persons/Joe/Parents/Parents?id={Joe.GrandparentID} จะคว้าคุณย่าของโจโดยมีตัวระบุที่คุณมีอยู่ในมือ

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

/Persons/Joe/Parents/Parents/Parents/Parents/Parents/Parents/Parents/Parents?id={Joe.NotableAncestor}

แต่สิ่งนี้นำไปสู่ตัวเลือกหลักที่สองสำหรับการแสดงข้อมูลนี้: ผ่านพารามิเตอร์พา ธ


2)ใช้พารามิเตอร์พา ธ เพื่อ "ค้นหาลำดับชั้น" คุณสามารถพัฒนาโครงสร้างต่อไปนี้เพื่อช่วยลดภาระของผู้บริโภคและยังมี API ที่เหมาะสม

หากต้องการมองย้อนกลับไป 147 รุ่นการแสดงตัวระบุทรัพยากรนี้พร้อมพารามิเตอร์พา ธ ช่วยให้คุณทำ

/Persons/Joe/Parents;generations=147?id={Joe.NotableAncestor}

หากต้องการค้นหา Joe จากปู่ย่าตายายที่ยิ่งใหญ่ของเขาคุณสามารถดูกราฟตามจำนวนรุ่นที่ทราบสำหรับ Joe's Id /Persons/JoesGreatGrandparent/Children;generations=3?id={Joe.Id}

สิ่งสำคัญที่ควรทราบเมื่อใช้วิธีการเหล่านี้คือหากไม่มีข้อมูลเพิ่มเติมในตัวระบุและคำขอคุณควรคาดหวังว่า URI แรกจะดึงข้อมูลบุคคล 147 รุ่นขึ้นไปจาก Joe ด้วยตัวระบุของ Joe.NotableAncestor คุณควรคาดหวังว่าอันที่สองจะเรียกโจ สมมติว่าสิ่งที่คุณต้องการคือการที่ลูกค้าโทรของคุณสามารถดึงชุดโหนดทั้งหมดและความสัมพันธ์ระหว่างรูต Person และบริบทสุดท้ายของ URI ของคุณ คุณสามารถทำได้ด้วย URI เดียวกัน (พร้อมการตกแต่งเพิ่มเติมบางอย่าง) และการตั้งค่าการยอมรับtext/vnd.graphvizในคำขอของคุณซึ่งเป็นประเภทสื่อที่ลงทะเบียน IANA สำหรับการ.dotแสดงกราฟ ด้วยการเปลี่ยน URI เป็น

/Persons/Joe/Parents;generations=147?id={Joe.NotableAncestor.Id}#directed

ด้วยส่วนหัวคำขอ HTTP Accept: text/vnd.graphviz และคุณสามารถให้ลูกค้าสื่อสารได้อย่างชัดเจนว่าพวกเขาต้องการกราฟกำกับของลำดับชั้นระหว่างโจและรุ่นที่ 147 ก่อนหน้านี้ที่รุ่นบรรพบุรุษรุ่นที่ 147 ประกอบด้วยบุคคลที่ถูกระบุว่าเป็น "บรรพบุรุษที่โดดเด่น" ของโจ

ฉันไม่แน่ใจว่า text / vnd.graphviz มี semantics ที่กำหนดไว้ล่วงหน้าสำหรับส่วนใด ๆ ของมันฉันไม่พบใครในการค้นหาคำแนะนำ หากประเภทสื่อนั้นจริงมีข้อมูลส่วนที่กำหนดไว้ล่วงหน้าแล้วความหมายของมันควรจะปฏิบัติตามเพื่อสร้าง URI สอดคล้อง แต่ถ้าซีแมนทิกส์เหล่านั้นไม่ได้ถูกกำหนดไว้ล่วงหน้าสเปค URI ระบุว่าซีแมนทิกส์ของตัวระบุชิ้นส่วนไม่มีข้อ จำกัด และถูกกำหนดโดยเซิร์ฟเวอร์แทนทำให้การใช้งานนี้ถูกต้อง


3) สตริงการสืบค้นที่ใช้จริงๆนอกเหนือจาก "การกรอง" ในทรัพยากรของคุณคืออะไร หากคุณใช้วิธีแรกพารามิเตอร์ตัวกรองจะถูกฝังใน URI เองเป็นพารามิเตอร์พา ธ แทนที่จะเป็นพารามิเตอร์สตริงข้อความค้นหา

ฉันเชื่อว่าฉันได้เอาชนะสิ่งนี้จนตายแล้ว แต่สตริงการสืบค้นไม่ได้สำหรับทรัพยากร "การกรอง" พวกเขามีไว้สำหรับระบุทรัพยากรของคุณจากข้อมูลที่ไม่ใช่ลำดับชั้น หากคุณเจาะลำดับชั้นของคุณด้วยเส้นทางของคุณโดยไป /person/{id}/children/และคุณต้องการระบุเด็กที่เฉพาะเจาะจงหรือชุดของเด็กที่เฉพาะเจาะจงคุณจะใช้คุณลักษณะบางอย่างที่ใช้กับชุดที่คุณระบุและรวมไว้ในแบบสอบถาม


1
RFC เกี่ยวข้องเฉพาะกับลำดับชั้นตราบเท่าที่มันกำหนดไวยากรณ์และอัลกอริทึมสำหรับการแก้ไขการอ้างอิง URI ที่เกี่ยวข้อง คุณช่วยอธิบายหรืออ้างอิงบางแหล่งที่อธิบายว่าทำไมตัวอย่างในโพสต์ต้นฉบับไม่สอดคล้องกันได้หรือไม่?
user2313838

2
ไม่ใช่แผนภูมิต้นไม้จริง ๆ ไม่ใช่กราฟไม่ใช่ต้นไม้ แต่ไม่ใช่แบบลำดับชั้น พิจารณาผู้ปกครองหลายคนการหย่าร้างและการแต่งงานใหม่ ฯลฯ
Myster

1
@RobertoAloi ดูเหมือนว่าฉันจะสื่อสารกับอินเทอร์เฟซ "ไม่พบรายการ" ของคุณเองผ่านทางชุดว่างเปล่าเมื่อ HTTP มีคำจำกัดความสำหรับเรื่องนี้อยู่แล้ว หลักการพื้นฐานคือคุณกำลังขอให้เซิร์ฟเวอร์ส่งคืน "สิ่งของ" และหากไม่มี "สิ่งของ (s)" ที่จะส่งคืนเซิร์ฟเวอร์จะสื่อสารกับ "404 - ไม่พบ" สิ่งนั้นขัดกับอะไร?
เค. อลันเบตส์

1
ฉันเชื่อเสมอ 404 เพื่อระบุว่าไม่พบ URL รูทของทรัพยากรนั่นคือการรวบรวมโดยรวม ดังนั้นถ้าคุณสอบถาม / หนังสือ? ผู้เขียน = จิมและไม่มีหนังสือโดยจิมคุณจะได้รับชุดที่ว่าง [] แต่ถ้าคุณสอบถาม / บทความ? ผู้เขียน = จิม แต่ทรัพยากรของบทความไม่ได้มีอยู่เป็นคอลเลกชัน 404 จะช่วยในการระบุว่าไม่มีประโยชน์ในการค้นหาบทความใด ๆ เลย
adjenks

1
@adjenks คุณไม่ได้เรียนรู้จากข้อกำหนดที่เป็นทางการ โครงสร้างส่วนประกอบของ url สามารถคิดได้ว่ามี "root" ถ้าช่วยให้คุณมีเหตุผลเกี่ยวกับวัตถุประสงค์ของส่วนที่เป็นส่วนประกอบ แต่ท้ายที่สุดสตริงแบบสอบถามไม่ได้เป็นตัวกรองการแสดงผลกับทรัพยากรที่ระบุผ่านเส้นทาง สตริงการสืบค้นเป็นพลเมืองชั้นหนึ่งของ url เมื่อคุณไม่พบทรัพยากรบนเซิร์ฟเวอร์ที่ตรงกับ url ของคุณ (รวมถึงสตริงแบบสอบถาม) 404 เป็นวิธีที่กำหนดไว้ภายใน http เพื่อสื่อสารสิ่งนี้ รูปแบบการโต้ตอบที่คุณโพสท่านำเสนอความแตกต่างโดยไม่มีความแตกต่าง
K. Alan Bates

14

นี่คือที่ที่คุณเข้าใจผิด:

หากลูกค้าของฉันผ่านฉันอ้างอิงรหัส

ในระบบ REST ลูกค้าไม่ควรใส่ใจกับ ID ตัวระบุทรัพยากรเท่านั้นที่ลูกค้าควรรู้ควรเป็น URIs นี่คือหลักการของ "ชุดอินเตอร์เฟส"

คิดว่าลูกค้าจะโต้ตอบกับระบบของคุณอย่างไร /grandparent/123กล่าวว่าผู้ใช้เริ่มการเรียกดูผ่านรายการของปู่ย่าตายายของเขาเลือกอย่างใดอย่างหนึ่งของเด็กของปู่ย่าตายายที่นำเขาไป หากลูกค้าควรจะสามารถค้นหาลูกของ/grandparent/123แล้วตาม "HATEOAS" สิ่งที่กลับมาเมื่อคุณทำแบบสอบถาม/grandparent/123ควรกลับ URL ไปยังอินเตอร์เฟซการค้นหา URL นี้ควรมีข้อมูลอะไรก็ตามที่จำเป็นสำหรับการกรองโดยปู่ย่าตายายปัจจุบันที่ฝังอยู่ในนั้น

ไม่ว่าจะเป็นการเชื่อมโยงที่มีลักษณะเหมือน/grandparent/123?search={term}หรือ/parent?grandparent=123&search={term}หรือ/parent?grandparentTerm=someterm&someothergplocator=blah&search={term}มีเล็กน้อยตาม REST โปรดสังเกตว่า URL เหล่านั้นทั้งหมดมีพารามิเตอร์เท่ากันซึ่งก็คือ{term}แม้ว่าพวกเขาจะใช้เกณฑ์ที่แตกต่างกัน คุณสามารถสลับระหว่าง URL ใด ๆ เหล่านั้นหรือผสมกันขึ้นอยู่กับปู่ย่าตายายที่เฉพาะเจาะจงและลูกค้าจะไม่แตกเพราะความสัมพันธ์เชิงตรรกะระหว่างทรัพยากรเหมือนกันแม้ว่าการใช้งานพื้นฐานอาจแตกต่างกันอย่างมีนัยสำคัญ

หากคุณได้สร้างบริการขึ้นมาแบบที่มันต้องการ/grandparent/{grandparentID}?search={term}เมื่อคุณไปทางเดียว แต่/children?parent={parentID}&search={term}a} เมื่อคุณไปอีกวิธีหนึ่งนั่นคือการมีเพศสัมพันธ์มากเกินไปเพราะลูกค้าจะต้องรู้ที่จะสอดแทรกสิ่งที่แตกต่างกันในความสัมพันธ์ที่แตกต่างกัน

ไม่ว่าคุณจะไปด้วย/grandparent/123?search={term}หรือ/parent?grandparent=123&search={term}เป็นเรื่องของรสนิยมและการดำเนินการใดก็ตามที่ง่ายขึ้นสำหรับคุณในตอนนี้ สิ่งสำคัญคือไม่ต้องให้ลูกค้าแก้ไขหากคุณเปลี่ยนกลยุทธ์ URL ของคุณหรือหากคุณใช้กลยุทธ์ที่แตกต่างกันในความสัมพันธ์ระหว่างพ่อแม่กับลูก


10

ฉันไม่แน่ใจว่าทำไมผู้คนคิดว่าการใส่ค่า ID ใน URL หมายถึง REST API อย่างใด REST นั้นเกี่ยวกับการจัดการคำกริยาการส่งผ่านทรัพยากร

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

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

ตัวอย่างเช่นฟีลดิงใช้การร้องขอรายงานทางวิชาการเป็นตัวอย่าง คุณต้องการเรียกคืนทรัพยากร "Dr John's Paper ในการดื่มเบียร์" แต่คุณอาจต้องการรุ่นเริ่มต้นหรือรุ่นล่าสุดดังนั้นตัวระบุทรัพยากรอาจไม่ใช่สิ่งที่อ้างอิงได้ง่ายว่าเป็น ID เดียวที่สามารถ วางไว้ใน URI REST อนุญาตให้ทำสิ่งนี้และความตั้งใจที่ระบุไว้ข้างหลังคือ:

ส่วนที่เหลืออาศัยแทนผู้เขียนเลือกตัวระบุทรัพยากรที่เหมาะกับธรรมชาติของแนวคิดที่ถูกระบุ

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

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

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

ในใจของฉันอ่านคำนิยามของ REST คุณกำลังร้องขอทรัพยากรลูกผ่านข้อมูลการควบคุม (ในรูปแบบของการสอบถาม) เพื่อกำหนดว่าคุณต้องการส่งคืนใด เป็นผลให้คุณไม่สามารถร้องขอ URI สำหรับปู่ย่าตายายหรือผู้ปกครอง คุณต้องการให้เด็ก ๆส่งคืนดังนั้นคำว่า parent / grandparent หรือ id ไม่ควรอยู่ใน URI ดังกล่าวอย่างแน่นอน เด็ก ๆ ควรจะเป็น


ที่จริงแล้ว URI ในฐานะที่เป็นแนวคิดเป็นศูนย์กลางของ REST สิ่งที่ไม่สำคัญเท่ากับว่าเป็นไวยากรณ์ของ URI ส่วนที่เหลือ REST ที่เหมาะสมจะต้องระบุทรัพยากรที่มีไวยากรณ์ทั่วไป ที่หลักการพื้นฐานของ REST ไม่จำเป็นต้องระบุทรัพยากรที่ซับซ้อนด้วยวัตถุ JSON แทน RFC 3986 URI แม้ว่าคุณจะทำเช่นนั้นคุณควรใช้ JSON RI สำหรับ API ทั้งหมดของคุณ
Lie Ryan

4

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

ทำไมปู่ย่าตายายถึงแตกต่างจากพ่อ พวกเขาทั้งคู่มีลูกที่อาจมีลูกที่สามารถ ...

ในที่สุดพวกเขาทั้งหมดเป็น 'มนุษย์' คุณอาจมีในรหัสของคุณเช่นกัน ดังนั้นใช้ที่:

GET /<api-endpoint>/humans/<ID>

จะส่งคืนข้อมูลที่มีประโยชน์เกี่ยวกับมนุษย์ (ชื่อและสิ่งของ)

GET /<api-endpoint>/humans/<ID>/children

จะส่งคืนชุดเด็กอย่างชัดเจน หากไม่มีเด็กอยู่อาร์เรย์ว่างอาจเป็นวิธีที่จะไป

เพื่อให้ง่ายเช่นคุณสามารถเพิ่มการตั้งค่าสถานะ 'hasChildren' หรือ 'hasGrandChildren'

คิดอย่างชาญฉลาดไม่ยาก


1

ต่อไปนี้เป็น RESTfull มากกว่าเนื่องจาก grandparentID ทุกตัวจะได้รับ URL ของตัวเอง วิธีนี้ทรัพยากรได้รับการระบุในลักษณะที่ไม่ซ้ำกัน

GET /myservice/api/v1/grandparents/{grandparentID}
GET /myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text}

การค้นหาพารามิเตอร์เคียวรีเป็นวิธีที่ดีในการดำเนินการค้นหาจากบริบทของทรัพยากรนั้น

เมื่อครอบครัวเริ่มมีขนาดใหญ่มากคุณสามารถใช้ start / limit เป็นตัวเลือกการสืบค้นได้เช่น:

GET /myservice/api/v1/grandparents/{grandparentID}/children?start=1&limit=50

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

บางทีนี่อาจเป็นสิ่งที่อ่านได้ดีแม้ว่าhttp://www.foretworks.com/insights/blog/rest-api-design-resource-modelingและวิทยานิพนธ์ปริญญาเอกดั้งเดิมของ Roy T Fielding https://www.ics.uci.edu / ~fielding/pubs/dissertation/fielding_dissertation.pdfที่อธิบายแนวคิดได้อย่างดีและสมบูรณ์


1

ฉันจะไปกับ

/myservice/api/v1/people/{personID}/descendants/2?search={text}

ในกรณีนี้คำนำหน้าทุกคำเป็นทรัพยากรที่ถูกต้อง:

  • /myservice/api/v1/people: ทุกคน
  • /myservice/api/v1/people/{personID}: บุคคลหนึ่งที่มีข้อมูลครบถ้วน (รวมถึงบรรพบุรุษพี่น้องลูกหลาน)
  • /myservice/api/v1/people/{personID}/descendants: ลูกหลานของบุคคลหนึ่ง
  • /myservice/api/v1/people/{personID}/descendants/2: ลูกหลานของคนคนหนึ่ง

อาจไม่ใช่คำตอบที่ดีที่สุด แต่อย่างน้อยก็เหมาะสมกับฉัน


-1

หลายคนก่อนที่ฉันจะชี้ให้เห็นแล้วว่ารูปแบบ URL นั้นไม่สำคัญในบริบทของบริการ RESTful ... สองเซ็นต์ของฉันจะเป็น ... สิ่งสำคัญของ REST ที่สนับสนุนในการเขียนต้นฉบับคือแนวคิดของ 'ทรัพยากร' .. . เมื่อคุณออกแบบ URL ของคุณคุณต้องจำไว้เสมอว่า 'ทรัพยากร' ไม่ใช่แถวในตารางเดียว (แม้ว่ามันอาจจะเป็น) .. แต่มันก็เป็นตัวแทน .. ที่สอดคล้องกัน .. . และสามารถใช้ในการเปลี่ยนแปลงสถานะของการเป็นตัวแทน (และมีประสิทธิภาพในการควบคุมสื่อเก็บข้อมูล) .. และทรัพยากรที่กำหนดอาจมีความหมายเฉพาะในบริบททางธุรกิจของคุณ .. ตัวอย่างเช่น;

ในตัวอย่าง / myservice / api / v1 / grandparents / {grandparentID} / parent / children? search = {text}

อาจเป็นทรัพยากรที่มีความหมายมากกว่านี้หากคุณย่อ / myservice / api / v1 / siblingsOfGrandParents นี้ให้สั้นลง? search = ..

ในกรณีนี้คุณสามารถประกาศ 'siblingsOfGrandParents' เป็นทรัพยากร .. ซึ่งจะทำให้ URL ง่ายขึ้น

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


เมื่อคุณใช้เวลาอันมีค่าในการลดคำตอบมันจะมีประโยชน์ถ้าคุณสามารถพูดและให้เหตุผลได้
Senthu Sivasambu

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