HATEOAS: URL ที่สมบูรณ์หรือสัมพัทธ์?


84

ในการออกแบบ RESTful Web Service โดยใช้ HATEOAS ข้อดีข้อเสียของการแสดงลิงก์เป็น URL แบบสมบูรณ์คืออะไร (" http: // server: port / application / customers / 1234 ") เทียบกับ just the path ("/ application / ลูกค้า / 1234 ")?

คำตอบ:


83

มีแนวคิดที่คลุมเครือเล็กน้อยเมื่อมีคนพูดว่า "URI ที่เกี่ยวข้อง"

ตามคำจำกัดความของ RFC3986 URI ทั่วไปประกอบด้วย:

  URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

  hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment

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

  1. แน่นอน URIหรือ URI เต็มรูปแบบ:"http://example.com:8042/over/there?name=ferret"
  2. และนี่คือuri สัมพัทธ์ที่มีเส้นทางสัมบูรณ์ :/over/there
  3. และนี่คือuri สัมพัทธ์กับเส้นทางสัมพัทธ์ : hereหรือ./hereหรือ../hereหรือ ฯลฯ

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

และในทางปฏิบัติเฟรมเวิร์ก MVC ฝั่งเซิร์ฟเวอร์ส่วนใหญ่สามารถสร้างURI สัมพัทธ์ด้วยพา ธ สัมบูรณ์เช่น/absolute/path/to/the/controllerและคำถามจะกลายเป็น "ว่าการติดตั้งเซิร์ฟเวอร์ควรนำscheme://hostname:portหน้าเส้นทางสัมบูรณ์" หรือไม่ ชอบคำถามของ OP ฉันไม่ค่อยแน่ใจเกี่ยวกับเรื่องนี้

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

ในทางกลับกันดูเหมือนว่าลูกค้าจะไม่สามารถเชื่อมต่อกับhttp://example.com:8042เส้นทางสัมบูรณ์ได้ ท้ายที่สุดไคลเอนต์รู้โครงร่างและชื่อโดเมนนั้นอยู่แล้วเมื่อส่งคำขอไปยังเซิร์ฟเวอร์ใช่ไหม

ทั้งหมดในทุกฉันจะบอกว่าขอแนะนำให้ใช้แน่นอน URI อาจจะเป็นทางเลือกที่จะ URI สัมพันธ์กับเส้นทางที่แน่นอนไม่เคยใช้เส้นทางสัมพัทธ์


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

ขอบคุณสำหรับความคิดเห็น ฉันแค่ยืมเส้นทางสัมบูรณ์และแนวคิดเส้นทางสัมพัทธ์จากระบบไฟล์ คำที่แตกต่างกันฉันไม่เห็นความแตกต่างที่สำคัญระหว่างความคิดเห็นของคุณกับของฉัน คุณยังแนะนำแบบฟอร์ม 1 & 2 และคุณต่อต้านแบบฟอร์ม 3 ใช่หรือไม่?
RayLuo

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

โดยส่วนตัวแล้วฉันไม่ (ยัง) เลยเชื่อว่า HATEOAS ดังนั้นความต้องการในการส่งคืน URI จึงมีความหมายสำหรับ API ฉันไม่เห็นว่า API ของฉันถูกขับเคลื่อนบนไคลเอนต์ในลักษณะที่คล้ายกับการเรียกดูเว็บไซต์ กรณีการใช้งานดูเหมือนขับเคลื่อนด้วยฟังก์ชัน adhoc เป็นอย่างมาก
Lawrence Dol

@LawrenceDol ฉันมีความสับสนเหมือนกันเกี่ยวกับ HATEOAS ในตอนเริ่มต้น ตอนนี้ฉันถือว่าเป็นเรื่องของทางเลือก ลูกค้าของคุณสามารถใช้ฟังก์ชัน adhoc เพื่อใช้ API ของคุณได้อย่างแน่นอน แต่ถ้าพวกเขา / คุณต้องการพวกเขา / คุณยังสามารถพัฒนารูปแบบให้พวกเขาปฏิบัติตามได้เพื่อที่ไคลเอนต์จะไม่ต้องฮาร์ดโค้ดแต่ละ url ที่แน่นอน นั่นคือ HATEOAS
RayLuo

13

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

อย่างไรก็ตามหากคุณกำลังสร้างเซิร์ฟเวอร์และคาดหวังให้คนอื่นเขียนโค้ดไคลเอ็นต์พวกเขาจะรักคุณมากขึ้นหากคุณให้ URI ที่สมบูรณ์ การแก้ไข URI แบบสัมพัทธ์อาจเป็นเรื่องยุ่งยากเล็กน้อย ก่อนอื่นคุณจะแก้ไขอย่างไรขึ้นอยู่กับประเภทสื่อที่ส่งคืน Html มีแท็กฐาน Xml สามารถมี xml: แท็กฐานในทุกองค์ประกอบที่ซ้อนกันฟีด Atom อาจมีฐานในฟีดและฐานอื่นในเนื้อหา หากคุณไม่ได้ให้ข้อมูลที่ชัดเจนเกี่ยวกับ URI พื้นฐานแก่ลูกค้าของคุณพวกเขาจะต้องได้รับ URI พื้นฐานจาก URI คำขอหรืออาจมาจากส่วนหัว Content-Location! และระวังการเฉือนต่อท้าย URI พื้นฐานถูกกำหนดโดยละเว้นอักขระทั้งหมดทางด้านขวาของเครื่องหมายทับสุดท้าย ซึ่งหมายความว่าตอนนี้เครื่องหมายทับมีความสำคัญมากเมื่อแก้ไข URI สัมพัทธ์

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


11

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


7

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


โดยให้คุณกำหนด "สัมบูรณ์" เป็นเส้นทางสัมบูรณ์ (เช่น/xxx/yyy...) และไม่ใช่ความหมาย URI ที่มีคุณสมบัติครบถ้วน (เช่นhttp://api.example.com/xxx/yyy...)
Lawrence Dol

6

การใช้Trichotomy ของ RayLouองค์กรของฉันเลือกที่จะชื่นชอบ (2) เหตุผลหลักคือเพื่อหลีกเลี่ยงการโจมตี XSS (Cross-Site Scripting) ปัญหาคือหากผู้โจมตีสามารถแทรกรูท URL ของตนเองลงในการตอบกลับที่กลับมาจากเซิร์ฟเวอร์คำขอของผู้ใช้ที่ตามมา (เช่นคำขอรับรองความถูกต้องด้วยชื่อผู้ใช้และรหัสผ่าน) จะถูกส่งต่อไปยังเซิร์ฟเวอร์ของผู้โจมตีเอง *

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

* โปรดแจ้งให้เราทราบหากมีข้อบกพร่องในการให้เหตุผลบรรทัดนี้ แน่นอนว่าเป้าหมายไม่ใช่เพื่อป้องกันการโจมตีทั้งหมด แต่ต้องมีการโจมตีอย่างน้อยหนึ่งช่องทาง


ดีใจที่คำตอบก่อนหน้านี้เป็นประโยชน์กับองค์กรของคุณ ใช่ฉันเองก็ชอบ (2) หรือที่เรียกว่าเส้นทางที่ไม่มีแบบแผน อย่างไรก็ตามฉันอยากรู้เกี่ยวกับเหตุผลของคุณ คุณบังคับให้ลูกค้าของคุณยอมรับเฉพาะ url แบบไม่ต้องใช้โครงการของคุณได้อย่างไร ไคลเอนต์ทั่วไปเช่นเบราว์เซอร์จะไม่ปฏิเสธ url แบบไม่ใช้แบบแผนเลย ดังนั้นฉันถือว่าคุณจะต้องเขียนโค้ดฝั่งไคลเอ็นต์ของคุณเองเพื่อตรวจสอบความถูกต้องของ URL ก่อนที่จะติดตามจริงหรือ แม้ว่าจะทำได้ในทางเทคนิค (แต่ไม่จำเป็นต้องมีประโยชน์) แต่โดยทั่วไปแล้วการตรวจสอบฝั่งไคลเอ็นต์แบบนี้ไม่ได้เป็นส่วนหนึ่งของการอภิปราย REST หรือ HATEOAS
RayLuo

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

5

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

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


1
ข้อมูลจำเพาะ HTTP สำหรับส่วนหัวตำแหน่งบอกว่า URI แน่นอน URI สัมบูรณ์ต้องมีโครงร่าง (เช่น http)
Mark Bober

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

คุณช่วยส่งลิงค์ไปยังส่วนของ spec ที่คุณกำลังพูดถึงได้ไหม?
Mark Bober

URI สัมบูรณ์ระบุโครงร่าง URI ที่ไม่สมบูรณ์ถูกกล่าวว่าเป็นญาติ URI ยังถูกจัดประเภทตามว่าเป็นแบบทึบหรือแบบลำดับชั้น URI ทึบแสงคือ URI สัมบูรณ์ซึ่งส่วนเฉพาะของโครงร่างไม่ได้ขึ้นต้นด้วยอักขระสแลช ('/') URI แบบทึบจะไม่ถูกแยกวิเคราะห์เพิ่มเติม ตัวอย่างบางส่วนของ URI แบบทึบ ได้แก่ mailto: java-net@java.sun.com news: comp.lang.java urn: isbn: 096139210x
Mark Bober

1
เฮ้ผู้ชายไม่ต้องกังวล อีกประเด็นหนึ่งเกี่ยวกับสิ่งนี้คือฉันเคยเห็นคนใช้ hrefs เป็น ID เพื่อให้ไคลเอนต์ไม่จำเป็นต้องสร้าง URL ใหม่จากไฟล์กำหนดค่าและ id บางไฟล์เพียงแค่รู้ URL และสามารถแคชตามมันได้
Mark Bober

2

การพิจารณาที่สำคัญในผลลัพธ์ API ขนาดใหญ่คือค่าใช้จ่ายของเครือข่ายเพิ่มเติมในการรวม URI แบบเต็มซ้ำ ๆ เชื่อหรือไม่ว่า gzip ไม่สามารถแก้ปัญหานี้ได้ทั้งหมด (ไม่แน่ใจว่าทำไม) เราตกใจมากที่ URI เต็มพื้นที่ใช้เวลาเท่าใดเมื่อมีลิงก์หลายร้อยลิงก์รวมอยู่ในผลลัพธ์


2

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

เอาคืน ... ไม่จริง. คุณควรไปหา URL แบบเต็มรวมทั้งโดเมน


3
เหตุใด URI ที่สมบูรณ์จึงใช้ชื่อโฮสต์ของพร็อกซีไม่ได้
Ed Summers

1
กำลังดำเนินการแก้ไขปัญหานี้ในขณะนี้ เราต้องการให้คำขอทั้งหมดผ่านเลเยอร์ "การจัดสรรภาระงาน" ก่อน URI สัมบูรณ์ไปยังเซิร์ฟเวอร์โดยตรงจะทำลายโมเดลนี้
mag382

1
ฉันใช้ Nginx เพื่อพร็อกซีไซต์ที่มี URL ที่สมบูรณ์ สามารถแทนที่ URL แบ็กเอนด์ด้วย URL พร็อกซีที่เทียบเท่าได้อย่างสมบูรณ์แบบ โดยเฉพาะอย่างยิ่งมันเป็น proxing windyroad.artifactoryonline.com (ซึ่งมี URL ที่ผ่านการรับรองและการเปลี่ยนเส้นทางที่มีคุณสมบัติครบถ้วน) ไปยังrepo.windyroad.com.au
Tom Howard

2

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

เกี่ยวกับข้อเสียฉันเห็นว่าสูญเสียการควบคุมเกี่ยวกับวิธีที่คุณสามารถกำหนดทิศทางการไหลของไคลเอนต์ระหว่างทรัพยากรในอนาคต (การทำโหลดบาลานซ์, การทดสอบ A / B, ... ) และฉันคิดว่าเป็นแนวทางปฏิบัติที่ไม่ดีเกี่ยวกับการจัดการเว็บ API URL ที่คุณระบุจะไม่ทึบแสงสำหรับไคลเอ็นต์อีกต่อไป (ดู Tim Berners-Lee Axioms of Web Architecture เกี่ยวกับความทึบ URI ) ในท้ายที่สุดคุณต้องรับผิดชอบในการทำให้ลูกค้าพึงพอใจเกี่ยวกับการใช้งาน API ของคุณอย่างสร้างสรรค์แม้ว่าจะเกี่ยวข้องกับโครงสร้างของพื้นที่ URL ของคุณก็ตาม หากคุณจำเป็นต้องอนุญาตให้มีการปรับเปลี่ยน URL ที่กำหนดไว้อย่างชัดเจนพิจารณาการใช้งานของแม่แบบ URIที่ใช้ในHypertext ประยุกต์ใช้ภาษา

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