เว็บแอปพลิเคชันเป็นไคลเอนต์ REST API: วิธีจัดการตัวระบุทรัพยากร


21

แนวคิดหลายอย่างที่เกี่ยวข้องกับความขัดแย้ง REST ในหัวของฉันเมื่อฉันลองใช้มัน

ฉันมี REST-ful back-end API ระบบที่เก็บตรรกะทางธุรกิจและเว็บแอปพลิเคชันที่มี UI จากแหล่งข้อมูลต่างๆเกี่ยวกับ REST (โดยเฉพาะอย่างยิ่งส่วนที่เหลือในการปฏิบัติ: Hypermedia และระบบสถาปัตยกรรม ) ฉันรู้ว่าฉันไม่ควรเปิดเผยตัวระบุดิบของหน่วยงานของฉัน rel="self"แต่กลับเชื่อมโยงหลายมิติด้วย

พิจารณาตัวอย่าง REST api มีทรัพยากรที่ส่งคืนบุคคล:

<Person>
  <Links>
    <Link rel="self" href="http://my.rest.api/api/person/1234"/>
  </Links>
  <Pets>
    <Link rel="pet" href="http://my.rest.api/api/pet/678"/>
  </Pets>
</Person>

ปัญหาเกิดขึ้นกับเว็บแอปพลิเคชัน สมมติว่ามันส่งคืนหน้าเว็บที่มีไฮเปอร์ลิงก์ไปยังเบราว์เซอร์:

<body class="person">
  <p>
    <a href="http://my.web.app/pet/???????" />
  </p>
</body>

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

ข้อกำหนดดูเหมือนขัดแย้งกัน:

  1. การเชื่อมโยงหลายมิติhrefควรนำไปสู่เว็บแอปพลิเคชันเนื่องจากเป็นระบบที่โฮสต์ UI
  2. The hrefควรมี id ของเอนทิตีเนื่องจากเว็บแอปต้องสามารถระบุเอนทิตีเมื่อหน้าเป้าหมายเปิดขึ้น
  3. เว็บแอปไม่ควรแยก / สร้าง REST URL เพราะไม่ใช่ REST-ful หนังสือดังกล่าวกล่าว

URIs ควรทึบแสงต่อผู้บริโภค มีเพียงผู้ออก URI เท่านั้นที่รู้วิธีตีความและแมปกับทรัพยากร

ดังนั้นฉันจึงไม่สามารถใช้เวลาเพียงแค่1234จาก URL ตอบสนอง API http://my.rest.api/api/AGRIDd~ryPQZ^$RjEL0jเพราะเป็นลูกค้าสงบฉันควรจะรักษามันราวกับว่ามันเป็นสิ่งที่ชอบ ในทางกลับกันฉันต้องให้ URL ที่นำไปสู่เว็บแอปของฉันและเพียงพอสำหรับแอปที่จะคืนค่า URL ดั้งเดิมของ API และใช้ URL นั้นเพื่อเข้าถึงทรัพยากร API

วิธีที่ง่ายที่สุดอาจเป็นเพียงการใช้ URL ของทรัพยากร API เป็นตัวระบุสตริง แต่ URL ของหน้าเว็บhttp://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234นั้นน่าเกลียด

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

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

  1. แทนที่โฮสต์ใน API URL และเก็บผลลัพธ์ไว้เท่านั้น ข้อเสียใหญ่คือมันต้องใช้เว็บแอปพลิเคชันเพื่อจัดการกับสิ่งที่ URL ที่ API สร้างขึ้นซึ่งหมายถึงการมีเพศสัมพันธ์ที่ชั่วร้าย ยิ่งไปกว่านั้นมันไม่สงบอีกต่อไปเพราะเว็บแอปของฉันเริ่มตีความ URL
  2. เปิดเผยรหัสดิบใน REST API พร้อมกับลิงก์ใช้เพื่อสร้าง URL ของ Web App จากนั้นใช้รหัสบนเซิร์ฟเวอร์เว็บแอปเพื่อค้นหาทรัพยากรที่จำเป็นใน API สิ่งนี้ดีกว่า แต่จะส่งผลต่อประสิทธิภาพการทำงานของเซิร์ฟเวอร์เว็บแอปเนื่องจากเว็บแอปจะต้องผ่านการนำทางบริการ REST ที่ออกคำขอลูกโซ่แบบขอเป็นรายบุคคลเพื่อจัดการกับคำขอใด ๆ จากเบราว์เซอร์ สำหรับทรัพยากรที่ค่อนข้างซ้อนกันอาจมีค่าใช้จ่ายสูง
  3. เก็บselfURL ทั้งหมดที่ส่งคืนโดย api ในการจับคู่แบบถาวร (DB?) บนเว็บแอปเซิร์ฟเวอร์ สร้างรหัสบางส่วนสำหรับพวกเขาใช้รหัสเพื่อสร้าง URL หน้าเว็บแอปและเพื่อรับ URL ของทรัพยากรบริการ REST คือผมเก็บhttp://my.rest.api/pet/678ที่ไหนสักแห่ง URL กับคีย์ใหม่การพูด3และการสร้าง URL http://my.web.app/pet/3ของหน้าเว็บ ดูเหมือนว่าการนำ HTTP Cache ไปใช้ในบางประเภท ฉันไม่รู้ว่าทำไม แต่ดูเหมือนแปลกสำหรับฉัน

หรือทุกอย่างหมายความว่า RESTful API ไม่สามารถใช้เป็นแบ็กเอนด์สำหรับแอปพลิเคชันเว็บได้หรือไม่


1
มันไม่ชัดเจนในสิ่งที่คุณพยายามจะสำเร็จอาจเป็นเพราะความตั้งใจที่เรียบง่ายของคุณถูกปกคลุมอยู่ใต้เลเยอร์ของสถาปัตยกรรมที่คุณใส่เข้าด้วยกันดังนั้นจึงเป็นการยากที่จะพูดว่า "RESTful APIs" จะช่วยคุณได้จริงหรือไม่ จากสิ่งที่ฉันเข้าใจถึงปัญหาของคุณตัวเลือกที่ 2 เป็นวิธีที่ง่ายและใช้งานได้ "ปัญหา" ที่นี่มีอยู่ใน "RESTful API"RestIsJustSqlReinventedและแน่นอนคุณจะพบปัญหาเดียวกันเมื่อคุณพยายามดึงข้อมูลกราฟย่อยที่ซับซ้อนเพียงพอจาก RDBMS ใด ๆ ใช้แคชหรือการแสดงที่ปรับให้เหมาะกับคำค้นหาของคุณ
back2dos

คำตอบ:


5

แก้ไขเพื่อแก้ไขคำถามที่อยู่คำตอบก่อนหน้านี้ถูกลบออก

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

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

<Entity type="Pet">
    <Link rel="self" href="http://example.com/pets/1" />
    <Link rel="owner" href="http://example.com/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

ตอนนี้เราสามารถอัปเดตชื่อของสัตว์เลี้ยงนั้นจาก Spot เป็น Fido โดยไม่ต้องยุ่งกับ ID ทรัพยากรจริง ๆ ตลอดแอปพลิเคชัน ในทำนองเดียวกันเราสามารถอ้างถึงสัตว์เลี้ยงนั้นใน GUI ของเราด้วยสิ่งที่ชอบ:

http://example.com/GUI/pets/Spot

หากสัตว์เลี้ยงไม่มีความรู้สึกใด ๆ หากไม่มีเจ้าของ (หรือสัตว์เลี้ยงไม่ได้รับอนุญาตในระบบโดยไม่มีเจ้าของ) เราสามารถใช้เจ้าของเป็นส่วนหนึ่งของ "ตัวตน" ของสัตว์เลี้ยงในระบบ:

http://example.com/GUI/owners/John/pets/1 (สัตว์เลี้ยงตัวแรกในรายการสำหรับ John)

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

<Entity type="ResourceList">
    <Link rel="people" href="http://example.com/api/people" />
    <Link rel="pets" href="http://example.com/api/pets" />
</Entity>

ดังนั้นโดยการรู้จุดเข้าแรกใน API และไม่ประมวลผล URL ใด ๆ เพื่อหาตัวระบุระบบเราสามารถทำสิ่งนี้:

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

<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/1" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/1" />
        <Link rel="pet" href="http://example.com/api/pets/2" />
    </Pets>
    <UniqueName>John</UniqueName>
</Entity>
<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/2" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/3" />
    </Pets>
    <UniqueName>Jane</UniqueName>
</Entity>

GUI จะวนรอบแต่ละทรัพยากรและพิมพ์รายการสำหรับแต่ละคนโดยใช้ UniqueName เป็น "id":

<a href="http://example.com/gui/people/1">John</a>
<a href="http://example.com/gui/people/2">Jane</a>

ในขณะที่ทำสิ่งนี้มันยังสามารถประมวลผลแต่ละลิงก์ที่พบได้ด้วย "สัตว์เลี้ยง" และรับทรัพยากรสัตว์เลี้ยงเช่น:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/api/pets/1" />
    <Link rel="owner" href="http://example.com/api/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

ใช้สิ่งนี้มันสามารถพิมพ์ลิงค์เช่น:

<!-- Assumes that a pet can exist without an owner -->
<a href="http://example.com/gui/pets/Spot">Spot</a>

หรือ

<!-- Assumes that a pet MUST have an owner -->
<a href="http://example.com/gui/people/John/pets/Spot">Spot</a>

หากเราไปกับลิงค์แรกและสมมติว่าทรัพยากรรายการของเรามีลิงค์ที่มีความสัมพันธ์กับ "สัตว์เลี้ยง" การไหลของการควบคุมจะเป็นอย่างนี้ใน GUI:

  1. หน้าถูกเปิดและขอให้นำสัตว์เลี้ยงเข้า
  2. โหลดรายการทรัพยากรจากจุดเข้า API
  3. โหลดทรัพยากรที่เกี่ยวข้องกับคำว่า "สัตว์เลี้ยง"
  4. ดูแต่ละทรัพยากรจากการตอบสนอง "สัตว์เลี้ยง" และค้นหาสิ่งที่ตรงกับจุด
  5. แสดงข้อมูลสำหรับจุด

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


ขอบคุณไมค์ ฉันได้อัปเดตคำถามของฉันเพื่อให้ชัดเจนขึ้น ปัญหากับคำตอบของคุณคือฉันไม่สามารถตกลงลูกค้า REST สามารถแยกวิเคราะห์ URL ถ้าเป็นเช่นนั้นมันจะเข้าคู่กับ URL และนี่เป็นการละเมิดแนวคิดหลักอย่างหนึ่งของ REST: ลูกค้าควรใช้rels เพื่อเลือกลิงก์ แต่ไม่ควรถือว่ามีความรู้เกี่ยวกับโครงสร้างของ URL REST อ้างว่า API มีอิสระในการเปลี่ยน URL ตามที่ระบุหากrelยังคงเหมือนเดิม การแยก URL ทำให้เราใกล้ชิดกับ SOAP มากกว่าไปยัง REST
Pavel Gatilov

ขอขอบคุณอีกครั้ง. คุณได้อธิบายถึงวิธีการที่เราดำเนินการมาจนถึงตอนนี้ ในทางเราจะเปิดเผยตัวระบุ สิ่งเดียวคือเราพยายามเปิดเผยตัวบ่งชี้ธรรมชาติทุกครั้งที่ทำได้
Pavel Gatilov

6

ทั้งหมดนี้หมายความว่า RESTful API ไม่สามารถใช้เป็นแบ็กเอนด์สำหรับแอปพลิเคชันเว็บได้หรือไม่

ฉันท้าทายว่ามันคุ้มค่าหรือไม่ที่จะแยกความแตกต่างระหว่าง REST API และเว็บแอปพลิเคชัน ของคุณ "โปรแกรมประยุกต์บนเว็บ" ก็ควรจะสลับการแสดง (HTML) ของทรัพยากรเดียวกัน - ซึ่งก็คือการพูดว่าผมไม่เข้าใจว่าทำไมคุณคาดว่าจะเข้าถึงhttp://my.rest.api/...และhttp://my.web.app/...และที่พวกเขาจะพร้อมกันที่เหมือนกันและแตกต่างกัน

"ลูกค้า" ของคุณเป็นเบราว์เซอร์ในกรณีนี้และเข้าใจ HTML และ JavaScript นั่นคือเว็บแอปพลิเคชันในความคิดของฉัน ตอนนี้คุณอาจไม่เห็นด้วยและคิดว่าคุณเข้าถึงเว็บแอปพลิเคชันกล่าวว่าใช้ foo.com และเปิดเผยทุกอย่างอื่นผ่าน api.foo.com - แต่คุณต้องถามแล้ว foo.com ให้การเป็นตัวแทนของทรัพยากรอย่างไร "แบ็คเอนด์" ของ foo.com นั้นสามารถเข้าใจวิธีการค้นหาแหล่งข้อมูลจาก api.foo.com ได้อย่างสมบูรณ์แบบ แอปพลิเคชันบนเว็บของคุณกลายเป็นเพียงพร็อกซี - ไม่ต่างไปจากการพูดคุยกับ API อื่น (จากคนอื่น) ด้วยกัน

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

ดังนั้นเมื่อลูกค้าของคุณเข้าถึง my.web.app/pets/1จะรู้ว่าจะนำเสนอส่วนติดต่อสัตว์เลี้ยงเพราะนั่นคือสิ่งที่ถูกส่งกลับโดยแม่แบบฝั่งเซิร์ฟเวอร์หรือถ้ามันเป็นคำขอแบบอะซิงโครนัสสำหรับการเป็นตัวแทนอื่น ๆ (เช่น JSON หรือ XML) ส่วนหัวชนิดเนื้อหา .

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

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


ฉันไม่คาดหวังว่าเว็บแอปจะเป็นเพียงตัวแทนของ API มันสามารถมีความแตกต่างมากมายเช่นแสดงทรัพยากรลูกหลายอย่างพร้อมกับรูทหนึ่งในหน้าเดียว ฉันไม่ต้องการให้เว็บแอปพลิเคชันใส่รหัสภายในของที่เก็บข้อมูล api ถ้าคุณหมายถึงสิ่งนี้โดยบอกว่าฉันคาดหวังว่าทั้ง 2 ระบบจะเหมือนกัน ฉันไม่ได้กังวลกับการแสดงที่นี่ไม่ใช่ปัญหา คำถามคือ 'ฉันจะใส่ 3 my.web.app/pets/3โดยไม่ต้องแยกวิเคราะห์ REST API Urls' ได้อย่างไร
Pavel Gatilov

การแก้ไขการใช้ถ้อยคำของตัวเองอีกครั้ง: 'ฉันจะใส่ 3 ลงไปได้อย่างไรmy.web.app/pets/3โดยไม่ต้องแยกวิเคราะห์ URL ของทรัพยากร REST API ที่เกี่ยวข้องmy.rest.api/v0/persons/2/pets/3? หรือฉันจะใส่อะไร
Pavel Gatilov

ฉันคิดว่าคุณสับสนสถานะของลูกค้าด้วยการรับรองที่กำหนดสถานะนั้น คุณไม่ใส่3ในapp/pets/3เพราะapp/pets/3เป็นสีขาวขุ่นก็ชี้ไปยังสิ่งที่ต้องการทรัพยากร app เว็บของคุณ หากเป็นมุมมองที่ประกอบด้วยทรัพยากรอื่น ๆ (ในระบบอื่น - API ของคุณเป็นหนึ่งในนั้น) คุณจะต้องเก็บไฮเปอร์ลิงก์ไว้ในระบบเหล่านั้นภายในเซิร์ฟเวอร์แอปพลิเคชันเว็บแล้วดึงข้อมูลเหล่านั้นมาแก้ไขแทน เช่น JSON หรือ XML) จากนั้นให้บริการเป็นส่วนหนึ่งของการตอบกลับของคุณ
Doug

คิดแบบนี้ - ลืม API และแอพของคุณ สมมติว่าคุณต้องการสร้างเว็บไซต์ที่ให้คนเก็บโพสต์ Facebook และ Twitter ที่พวกเขาชื่นชอบ นั่นคือระบบระยะไกล คุณจะไม่พยายามอุโมงค์หรือแม่แบบ URIs กับระบบเหล่านั้นด้วยตัวคุณเอง คุณจะสร้างทรัพยากร 'บอร์ด' และมันจะเป็นเซิร์ฟเวอร์ของคุณที่รู้ว่าboard/1ชี้ไปที่facebook.com/post/123และtwitter.com/status/789เมื่อคุณไปเพื่อแสดงบอร์ดของคุณคุณจะต้องแก้ไข URIs เหล่านั้นให้เป็นตัวแทนที่คุณสามารถทำงานได้ แคชในกรณีที่จำเป็น
Doug

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

5

คำนำ

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


การค้นพบต้องการความรู้จำนวนหนึ่งดังนั้นนี่คือสถานการณ์ของฉันในโลกแห่งความจริง:

สมมติว่าเราต้องการหน้าค้นหาhttp://my.web.app/personที่ผลลัพธ์รวมลิงค์ไปยังหน้ารายละเอียดสำหรับแต่ละคน สิ่งหนึ่งที่รหัส front-end ของเราต้องรู้เพื่อที่จะทำอะไรเลยคือ URL http://my.rest.api/apiฐานสำหรับแหล่งข้อมูลส่วนที่เหลือของมัน การตอบสนองต่อคำขอ GET ต่อ URL นี้อาจเป็น:

<Links>
    <Link ref="self" href="http://my.rest.api/api" />
    <Link rel="person" href="http://my.rest.api/api/person" />
    <Link rel="pet" href="http://my.rest.api/api/pet" />
</Links>

เนื่องจากความตั้งใจของเราคือการแสดงรายชื่อคนเราจึงส่งGETคำขอไปยัง href จากpersonลิงก์ href ซึ่งอาจส่งคืน:

<Links>
    <Link ref="self" href="http://my.rest.api/api/person" />
    <Link rel="search" href="http://my.rest.api/api/person/search" />
</Links>

เราต้องการแสดงผลการค้นหาดังนั้นเราจะใช้บริการค้นหาโดยส่งGETคำขอไปยังsearchลิงก์ href ซึ่งอาจส่งคืน:

<Persons>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/1"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/10"/>
        </Pets>
    </Person>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/2"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/20"/>
        </Pets>
    </Person>
</Persons>

ในที่สุดเราก็มีผลลัพธ์ แต่เราจะสร้าง URL หน้าได้อย่างไร

ลองตัดส่วนที่เราทราบอย่างแน่นอน: URL ฐาน API และใช้ส่วนที่เหลือเป็นตัวระบุส่วนหน้าของเรา:

  • API ฐานที่รู้จักกัน: http://my.rest.api/api
  • URL ที่กำหนดสำหรับแต่ละรายการ: http://my.rest.api/api/person/1
  • ID ไม่ซ้ำกัน: /person/1
  • URL ฐานของเรา: http://my.web.app
  • URL หน้าของเราที่สร้างขึ้น: http://my.web.app/person/1

ผลลัพธ์ของเราอาจมีลักษณะดังนี้:

<ul>
    <li><a href="http://my.web.app/person/1">A person</a></li>
    <li><a href="http://my.web.app/person/2">A person</a></li>
</ul>

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

  • URL หน้า: http://my.web.app/person/1
  • URL ฐานของเรา: http://my.web.app
  • ID ไม่ซ้ำกัน: /person/1
  • API ฐานที่รู้จักกัน: http://my.rest.api/api
  • URL API ที่สร้าง: http://my.rest.api/api/person/1

หาก REST API เปลี่ยนแปลงเช่นว่าpersonตอนนี้มี URL http://my.rest.api/api/different-person-base/person/1และบางคนเคยทำบุ๊กมาร์กไว้ก่อนหน้านี้http://my.web.app/person/1REST API ควร (อย่างน้อยหนึ่งครั้ง) จัดเตรียมความเข้ากันได้แบบย้อนหลังโดยตอบสนองต่อ URL เก่าด้วยการเปลี่ยนเส้นทางไปยังใหม่ ลิงก์ส่วนหน้าทั้งหมดที่สร้างขึ้นจะรวมโครงสร้างใหม่โดยอัตโนมัติ

ดังที่คุณอาจสังเกตเห็นมีหลายสิ่งที่เราต้องรู้เพื่อนำทาง API:

  • API ฐาน URL
  • personความสัมพันธ์
  • searchความสัมพันธ์

ฉันไม่คิดว่าจะมีอะไรผิดปกติกับสิ่งนี้ เราไม่ได้สมมติโครงสร้าง URL เฉพาะ ณ จุดใด ๆ ดังนั้นโครงสร้างของเอนทิตี URL http://my.rest.api/api/person/1อาจเปลี่ยนแปลงได้และตราบใดที่ API ให้ความเข้ากันได้แบบย้อนหลังรหัสของเราจะยังใช้งานได้


คุณถามว่าตรรกะการกำหนดเส้นทางของเราสามารถบอกความแตกต่างระหว่างสอง URL หน้าได้อย่างไร:

  • http://my.rest.api/api/person/1
  • http://my.rest.api/api/pet/3.

ก่อนอื่นฉันจะชี้ให้เห็นว่าคุณใช้ฐาน API ในความคิดเห็นของคุณเมื่อในตัวอย่างของเราเราใช้ URL ฐานแยกต่างหากสำหรับ UI และ REST API ฉันจะทำตัวอย่างต่อโดยใช้เบสแยกกัน แต่การแชร์ฐานไม่ใช่ปัญหา เราสามารถ (หรือควรจะ) แผนที่วิธีการกำหนดเส้นทาง UI โดยใช้ประเภทสื่อจากส่วนหัวยอมรับของคำขอ

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

ก่อนหน้านี้ URL หน้าของเราอยู่ในรูปแบบ: ${UI base}/${opaque string id}

รูปแบบใหม่อาจเป็น: ${UI base}/${entity type}/${opaque string id}

ดังนั้นเมื่อใช้/person/1ตัวอย่างเราจะต้องจบลงด้วยhttp://my.web.app/person/person/1ตัวอย่างเช่นเราต้องการจบลงด้วย

ด้วยรูปแบบนี้ตรรกะการกำหนดเส้นทาง UI ของเราจะทำงานร่วมกับ /person/person/1และเมื่อรู้ว่าโทเค็นแรกในสตริงนั้นถูกแทรกด้วยตัวเราเองเราสามารถดึงมันออกมาและกำหนดเส้นทางไปยังหน้ารายละเอียดที่เหมาะสม (บุคคลในตัวอย่างนี้) ตาม หากคุณรู้สึกอายเกี่ยวกับ URL นั้นเราสามารถแทรกเข้าไปอีกเล็กน้อยได้ อาจจะ: http://my.web.app/person/detail/person/1

ในกรณีนี้เราจะแยกวิเคราะห์/person/detailหาเส้นทางและใช้ส่วนที่เหลือเป็นรหัสสตริงทึบแสง


ฉันคิดว่านี่เป็นการแนะนำการเชื่อมต่อที่แน่นหนาของเว็บแอปไปยัง API

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

ถ้าฉันต้องการโครงสร้าง URL สำหรับแอปพลิเคชันเว็บจะแตกต่างจาก API

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

จะทำอย่างไรถ้าเอนทิตีของแอปบนเว็บของฉันไม่ได้แม็ปกับเอนทิตี api 1-1

คำตอบขึ้นอยู่กับความสัมพันธ์ ไม่ว่าจะด้วยวิธีใดคุณจะต้องมีวิธีในการจับคู่ส่วนหน้ากับ URL ของ API


ฉันมีปัญหาหนึ่งกับวิธีนี้ จริงๆแล้วมันเป็นหมายเลข 1 ในรายการโซลูชั่นของฉัน สิ่งที่ฉันไม่ได้รับคือ: ถ้า app เว็บไม่ได้ตีความ URL ที่และถือว่ารหัสที่ไม่ซ้ำกันเป็นสตริงสีขาวขุ่น (เพียงperson/1, pet/3) แล้วว่ามันจะรู้ว่าถ้าเบราว์เซอร์จะเปิดhttp://my.rest.api/api/person/1มันควรจะแสดงคน UI และถ้ามัน เปิดhttp://my.rest.api/api/pet/3แล้วสัตว์เลี้ยง UI
Pavel Gatilov

คำถามที่ดี! ฉันได้อัพเดตคำตอบพร้อมกับคำตอบแล้ว
Mike Partridge

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

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

ความกังวลหลักของฉันที่นี่เป็นประโยชน์มากกว่าเล็กน้อย ฉันใช้ ASP.NET MVC เพื่อใช้งานเว็บแอปและเนื่องจากกฎภายในบางข้อฉันต้องกำหนดรูปแบบ URL ที่แอพรองรับ คือถ้ามีการกำหนด / a / {id} แอปจะจัดการ / a / 1 แต่ไม่ใช่ / a / 1 / b / 2 สิ่งนี้นำไปสู่ความต้องการในการคอมไพล์เว็บแอปใหม่หาก URL REST API เปลี่ยนไม่เพียง แต่จะเก็บรักษา URL ที่คั่นหน้าไว้เท่านั้น เพียงเพราะการเชื่อมโยงหลายมิติที่ฝังอยู่ในหน้า html ไม่สามารถทำได้หากไม่มี
Pavel Gatilov

2

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

ฉันไม่ควรเปิดเผยตัวบ่งชี้แบบดิบของเอนทิตีของฉัน แต่ให้ส่งกลับการเชื่อมโยงหลายมิติด้วย rel = "self"

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

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

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

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

ทั้งหมดนี้หมายความว่า RESTful API ไม่สามารถใช้เป็นแบ็กเอนด์สำหรับแอปพลิเคชันเว็บได้หรือไม่

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


0

สิ่งที่ฉันถ้าคุณยึดติดกับสิ่งที่คล้ายกับAtom Syndication Formatคุณเป็นสิ่งที่ดี

ข้อมูลเมตาที่อธิบายถึงรายการที่กำลังแสดงสามารถระบุได้โดยใช้องค์ประกอบ / คุณสมบัติเพิ่มเติม:

  • ตาม[RFC4287]มี URI ที่ระบุรายการโดยไม่ซ้ำกัน

  • ตาม[RFC4287]องค์ประกอบนี้เป็นตัวเลือก หากรวมมันมี URI ที่ลูกค้าควรใช้เพื่อดึงรายการ

นี่เป็นเพียงสองเซ็นต์ของฉัน


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

0

ไม่ต้องกังวลกับ URL กังวลเกี่ยวกับประเภทสื่อ

ดูที่นี่ (สัญลักษณ์หัวข้อย่อยที่สามโดยเฉพาะ)

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


ในกรณีของเว็บแอปทั่วไปที่ลูกค้าเป็นมนุษย์ ; เบราว์เซอร์เป็นเพียงตัวแทน

ดังนั้นแท็กสมอเช่น

          <a href="example.com/foo/123">click here</a>

สอดคล้องกับสิ่งที่ชอบ

          <link type="text/html" rel="self" href="example.com/foo/123">

URL ยังคงทึบสำหรับผู้ใช้ทั้งหมดที่เธอใส่ใจคือประเภทสื่อ (เช่น text/html, application/pdf, application/flv, video/x-flv, image/jpeg, image/funny-cat-picture etc ) ข้อความพรรณนาที่มีอยู่ในสมอ (และในแอตทริบิวต์ชื่อ) เป็นเพียงวิธีการขยายประเภทความสัมพันธ์ในแบบที่มนุษย์เข้าใจได้

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

สรุป

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


Rodrick คำถามของฉันไม่ได้เกี่ยวกับการสร้าง API แต่เป็นการสร้างเว็บแอปพลิเคชันที่อยู่ด้านบนของ RESTful API ฉันแทบจะไม่เข้าใจว่าประเภทสื่อสามารถช่วยฉันสร้าง URL สำหรับเว็บแอปได้อย่างไร แม้ว่าประเภทสื่อมีความสำคัญสำหรับสัญญาการบริการและการค้นพบ
Pavel Gatilov

@PavelGatilov - ลูกค้าของเว็บแอปของคุณเป็นมนุษย์หรือไม่?
Rodrick Chapman

ใช่แล้ว. และเป็นคนที่มีทักษะน้อย
Pavel Gatilov

0

วิธีที่ง่ายที่สุดอาจเป็นเพียงการใช้ URL ของทรัพยากร API เป็นตัวระบุสตริง แต่ URL ของหน้าเว็บเช่นhttp://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234นั้นน่าเกลียด

ฉันคิดว่าคุณพูดถูกนั่นเป็นวิธีที่ง่ายที่สุด คุณสามารถสร้างความสัมพันธ์กับ URL อีกครั้งhttp://my.rest.api/apiเพื่อทำให้URL น่าเกลียดน้อยลง:

http://my.web.app/person/person%2F1234

หาก URL ที่จัดทำโดย API นั้นไม่สัมพันธ์กับฐานนั้นก็จะลดลงไปในรูปแบบที่น่าเกลียด:

http://my.web.app/person/http%3A%2F%2Fother.api.host%2Fapi%2Fperson%2F1234

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

http://my.web.app/person/1234 (best case)
http://my.web.app/http://other.api.host/api/person/1234 (ugly case)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.