มีวิธีการหรือวิธีปฏิบัติที่ดีที่สุดสำหรับการวางเวอร์ชัน REST API สำหรับบริการบนเว็บหรือไม่?
ฉันได้พบว่าAWS ไม่เวอร์ชันโดย URL ของปลายทางที่ นี่เป็นวิธีเดียวหรือมีวิธีอื่นในการบรรลุเป้าหมายเดียวกันหรือไม่? หากมีหลายวิธีอะไรคือข้อดีของแต่ละวิธี?
มีวิธีการหรือวิธีปฏิบัติที่ดีที่สุดสำหรับการวางเวอร์ชัน REST API สำหรับบริการบนเว็บหรือไม่?
ฉันได้พบว่าAWS ไม่เวอร์ชันโดย URL ของปลายทางที่ นี่เป็นวิธีเดียวหรือมีวิธีอื่นในการบรรลุเป้าหมายเดียวกันหรือไม่? หากมีหลายวิธีอะไรคือข้อดีของแต่ละวิธี?
คำตอบ:
นี่เป็นคำถามที่ดีและยุ่งยาก หัวข้อของการออกแบบ URIในเวลาเดียวกันเป็นส่วนที่โดดเด่นที่สุดของ REST API และดังนั้นความมุ่งมั่นในระยะยาวที่อาจเกิดขึ้นกับผู้ใช้ของ APIนั้น
ตั้งแต่วิวัฒนาการของแอพลิเคชันและในระดับที่น้อยกว่า API ของมันคือความจริงของชีวิตและนั่นก็จะยิ่งคล้ายกับวิวัฒนาการของสินค้าที่มีความซับซ้อนดูเหมือนเช่นภาษาการเขียนโปรแกรมที่ออกแบบ URIควรมีน้อยจำกัด ธรรมชาติและมันควรจะเก็บรักษาไว้ เมื่อเวลาผ่านไป ยิ่งอายุการใช้งานของแอปพลิเคชั่นและ API ยาวนานขึ้นเท่าไหร่ความมุ่งมั่นของผู้ใช้แอปพลิเคชันและ API ก็ยิ่งมากขึ้นเท่านั้น
ในทางกลับกันความเป็นจริงของชีวิตก็คือว่ามันยากที่จะมองเห็นทรัพยากรทั้งหมดและแง่มุมของพวกเขาที่จะใช้ผ่าน API โชคดีที่มันไม่ได้เป็นสิ่งที่จำเป็นในการออกแบบ API ทั้งหมดซึ่งจะถูกนำมาใช้จนคติ มันเพียงพอที่จะกำหนดจุดสิ้นสุดของทรัพยากรได้อย่างถูกต้องและโครงร่างการกำหนดแอดเดรสของทุกทรัพยากรและอินสแตนซ์ของทรัพยากร
เมื่อเวลาผ่านไปคุณอาจต้องเพิ่มทรัพยากรใหม่และแอตทริบิวต์ใหม่ให้กับแต่ละทรัพยากรเฉพาะ แต่วิธีการที่ผู้ใช้ API ติดตามเพื่อเข้าถึงทรัพยากรเฉพาะไม่ควรเปลี่ยนแปลงเมื่อรูปแบบการกำหนดทรัพยากรกลายเป็นสาธารณะและสุดท้าย
วิธีนี้ใช้กับซีแมนทิกส์ HTTP verb (เช่น PUT ควรอัปเดต / แทนที่เสมอ) และรหัสสถานะ HTTP ที่ได้รับการสนับสนุนใน API เวอร์ชันก่อนหน้า (ควรทำงานต่อไปเพื่อให้ไคลเอนต์ API ที่ทำงานโดยปราศจากการแทรกแซงของมนุษย์ควรทำงานต่อไป เช่นนั้น).
นอกจากนี้เนื่องจากการฝัง API เวอร์ชันลงใน URI จะทำให้แนวคิดของสื่อหลายมิติเป็นกลไกของสถานะแอปพลิเคชัน (ระบุไว้ในวิทยานิพนธ์ปริญญาเอกของ Roy T. Fieldings PhD) โดยมีที่อยู่ทรัพยากร / URI ที่จะเปลี่ยนแปลงตลอดเวลาฉันจะสรุปว่าAPI รุ่นที่ไม่ควรเก็บไว้ใน URI ของทรัพยากรที่เป็นเวลานานมีความหมายว่าURI ของทรัพยากรที่ผู้ใช้ API สามารถขึ้นอยู่กับที่ควรจะเป็น Permalinks
แน่นอนว่าเป็นไปได้ที่จะฝังรุ่น API ใน URI ฐานแต่สำหรับการใช้งานที่สมเหตุสมผลและ จำกัด เช่นการดีบักไคลเอ็นต์ APIที่ทำงานกับเวอร์ชัน API ใหม่ API ที่มีเวอร์ชันดังกล่าวควร จำกัด เวลาและพร้อมใช้งานสำหรับกลุ่มผู้ใช้ API ที่ จำกัด (เช่นในช่วง Betas ปิด) เท่านั้น มิฉะนั้นคุณจะทำตัวเองในที่ที่ไม่ควรทำ
ความคิดสองสามข้อเกี่ยวกับการบำรุงรักษาเวอร์ชัน API ที่มีวันหมดอายุอยู่ แพลตฟอร์มการเขียนโปรแกรม / ภาษาที่ใช้กันทั่วไปในการใช้บริการเว็บ (Java,. NET, PHP, Perl, Rails และอื่น ๆ ) ช่วยให้เชื่อมโยงจุดสิ้นสุดของบริการเว็บเข้ากับ URI ฐานได้ง่าย วิธีนี้มันง่ายที่จะรวบรวมและให้เก็บไฟล์ / เรียน / วิธีแยกข้ามรุ่น API ที่แตกต่างกัน
จากผู้ใช้ API POV มันง่ายกว่าที่จะทำงานกับและผูกเข้ากับ API รุ่นใดรุ่นหนึ่งเมื่อเห็นได้ชัด แต่มีระยะเวลา จำกัด เท่านั้นเช่นในระหว่างการพัฒนา
จาก POV ผู้ดูแล API ของมันง่ายกว่าที่จะรักษารุ่น API ที่แตกต่างกันโดยใช้ระบบควบคุมแหล่งที่ทำงานส่วนใหญ่ในไฟล์เป็นหน่วยที่เล็กที่สุดของการกำหนดเวอร์ชัน (ซอร์สโค้ด)
อย่างไรก็ตามด้วยเวอร์ชัน API ที่มองเห็นได้ชัดเจนใน URI มีข้อแม้: หนึ่งอาจคัดค้านวิธีการนี้เนื่องจากประวัติ API จะมองเห็นได้ / aparent ในการออกแบบ URI และดังนั้นจึงมีแนวโน้มที่จะเปลี่ยนแปลงตลอดเวลาซึ่งขัดกับแนวทางของ REST ฉันเห็นด้วย!
วิธีที่จะหลีกเลี่ยงการคัดค้านที่สมเหตุสมผลนี้คือการใช้ API เวอร์ชันล่าสุดภายใต้ URI ฐาน API ที่ไม่มีเวอร์ชัน ในกรณีนี้ผู้พัฒนาไคลเอ็นต์ API สามารถเลือก:
พัฒนาเทียบกับเวอร์ชันล่าสุด (ยอมรับตนเองเพื่อรักษาแอปพลิเคชันที่ปกป้องมันจากการเปลี่ยนแปลง API ในที่สุดซึ่งอาจทำให้ไคลเอนต์ API ที่ออกแบบมาไม่ดี )
ผูกกับ API รุ่นเฉพาะ (ซึ่งเห็นได้ชัด) แต่ในระยะเวลาที่ จำกัด เท่านั้น
ตัวอย่างเช่นหาก API v3.0 เป็นเวอร์ชัน API ล่าสุดสองชื่อต่อไปนี้ควรเป็นชื่อแทน (เช่นทำงานตามคำขอ API ทั้งหมด):
http: // shonzilla / api / ลูกค้า / 1234 http: // shonzilla / api /v3.0 / ลูกค้า / 1234 http: // shonzilla / api / v3 / ลูกค้า / 1234
นอกจากนี้ลูกค้าของ API ที่ยังคงพยายามที่จะชี้ไปเก่า API ควรทราบการใช้ API รุ่นล่าสุดก่อนหน้านี้หากรุ่น API ที่พวกเขากำลังใช้เป็นล้าสมัยหรือไม่ได้รับการสนับสนุนอีกต่อไป ดังนั้นการเข้าถึง URIs ที่ล้าสมัยเช่นนี้:
http: // shonzilla / api /v2.2 / ลูกค้า / 1234 http: // shonzilla / api /v2.0 / ลูกค้า / 1234 http: // shonzilla / api / v2 / ลูกค้า / 1234 http: // shonzilla / api /v1.1 / ลูกค้า / 1234 http: // shonzilla / api / v1 / ลูกค้า / 1234
ควรส่งคืนรหัสสถานะ 30x HTTPใด ๆที่ระบุการเปลี่ยนเส้นทางที่ใช้ร่วมกับLocation
ส่วนหัว HTTP ที่เปลี่ยนเส้นทางไปยัง URI ทรัพยากรรุ่นที่เหมาะสมซึ่งยังคงเป็นรหัสนี้:
http: // shonzilla / API / ลูกค้า / 1234
มีรหัสสถานะ HTTP การเปลี่ยนเส้นทางอย่างน้อยสองตัวที่เหมาะสมกับสถานการณ์การกำหนดเวอร์ชัน API:
301 ย้ายอย่างถาวรบ่งชี้ว่าทรัพยากรที่มี URI ที่ร้องขอถูกย้ายอย่างถาวรไปยัง URI อื่น (ซึ่งควรเป็นลิงก์อินสแตนซ์ของทรัพยากรที่ไม่มีข้อมูลรุ่น API) รหัสสถานะนี้สามารถนำมาใช้เพื่อระบุล้าสมัย / รุ่นได้รับการสนับสนุน API แจ้งลูกค้า API ว่าทรัพยากร versioned URI ถูกแทนที่ด้วยความคิดเห็นทรัพยากร
302 พบว่าแสดงว่าทรัพยากรที่ร้องขอนั้นอยู่ในตำแหน่งอื่นชั่วคราวในขณะที่ URI ที่ร้องขออาจยังคงรองรับอยู่ รหัสสถานะนี้อาจมีประโยชน์เมื่อ URIs ที่ใช้เวอร์ชันน้อยไม่สามารถใช้งานได้ชั่วคราวและควรทำการร้องขอซ้ำโดยใช้ที่อยู่เปลี่ยนเส้นทาง (เช่นชี้ไปที่ URI พร้อมกับฝังรุ่น APi) และเราต้องการแจ้งให้ลูกค้าใช้งานต่อไป Permalinks)
สถานการณ์อื่น ๆ สามารถพบได้ในบทเปลี่ยนเส้นทาง 3xx ของข้อกำหนด HTTP 1.1
410 Gone
เนื่องจากการเปลี่ยนเส้นทางอาจบ่งบอกว่าตำแหน่งใหม่เข้ากันได้เมื่อไม่ได้ใช้ หาก API ล้าสมัย แต่ยังคงมีอยู่Warning
ส่วนหัว HTTP บนการตอบสนองอาจเป็นตัวเลือก
URL ไม่ควรมีรุ่น รุ่นนี้ไม่เกี่ยวข้องกับ "ความคิด" ของทรัพยากรที่คุณร้องขอ คุณควรลองคิดถึง URL ว่าเป็นเส้นทางไปสู่แนวคิดที่คุณต้องการไม่ใช่วิธีที่คุณต้องการให้ไอเท็มส่งคืน รุ่นสั่งการเป็นตัวแทนของวัตถุไม่ใช่แนวคิดของวัตถุ ดังที่ผู้โพสต์คนอื่นพูดไว้คุณควรระบุรูปแบบ (รวมถึงเวอร์ชั่น) ในส่วนหัวคำขอ
หากคุณดูคำร้องขอ HTTP แบบเต็มสำหรับ URL ที่มีเวอร์ชันดูเหมือนว่า:
(BAD WAY TO DO IT):
http://company.com/api/v3.0/customer/123
====>
GET v3.0/customer/123 HTTP/1.1
Accept: application/xml
<====
HTTP/1.1 200 OK
Content-Type: application/xml
<customer version="3.0">
<name>Neil Armstrong</name>
</customer>
ส่วนหัวมีบรรทัดที่มีตัวแทนที่คุณต้องการ ("ยอมรับ: application / xml") นั่นคือที่ที่รุ่นควรจะไป ดูเหมือนว่าทุกคนจะคัดค้านความจริงที่ว่าคุณอาจต้องการสิ่งเดียวกันในรูปแบบที่แตกต่างกันและลูกค้าควรจะสามารถถามสิ่งที่ต้องการ ในตัวอย่างด้านบนไคลเอ็นต์กำลังขอการแสดง XML ใด ๆของทรัพยากร - ไม่ใช่การแสดงที่แท้จริงของสิ่งที่ต้องการ ในทางทฤษฎีแล้วเซิร์ฟเวอร์สามารถส่งคืนสิ่งที่ไม่เกี่ยวข้องกับคำขอได้อย่างสมบูรณ์ตราบใดที่เป็น XML และจะต้องมีการแยกวิเคราะห์เพื่อรับรู้ว่ามันผิด
วิธีที่ดีกว่าคือ:
(GOOD WAY TO DO IT)
http://company.com/api/customer/123
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+xml
<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+xml
<customer>
<name>Neil Armstrong</name>
</customer>
นอกจากนี้สมมติว่าลูกค้าคิดว่า XML นั้นละเอียดเกินไปและตอนนี้พวกเขาต้องการ JSON แทน ในตัวอย่างอื่น ๆ คุณจะต้องมี URL ใหม่สำหรับลูกค้ารายเดียวกันดังนั้นคุณจะต้อง:
(BAD)
http://company.com/api/JSONv3.0/customers/123
or
http://company.com/api/v3.0/customers/123?format="JSON"
(หรืออะไรทำนองนั้น) เมื่อใดก็ตามที่คำขอ HTTP ทั้งหมดมีรูปแบบที่คุณต้องการ:
(GOOD WAY TO DO IT)
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+json
<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+json
{"customer":
{"name":"Neil Armstrong"}
}
เมื่อใช้วิธีนี้คุณจะมีอิสระในการออกแบบมากขึ้นและยึดมั่นในแนวคิดดั้งเดิมของ REST คุณสามารถเปลี่ยนรุ่นได้โดยไม่รบกวนลูกค้าหรือเปลี่ยนลูกค้าเพิ่มเติมเมื่อมีการเปลี่ยนแปลง API หากคุณเลือกที่จะหยุดสนับสนุนการเป็นตัวแทนคุณสามารถตอบกลับคำขอด้วยรหัสสถานะ HTTP หรือรหัสที่กำหนดเอง ลูกค้ายังสามารถตรวจสอบการตอบสนองในรูปแบบที่ถูกต้องและตรวจสอบ XML
มีข้อดีอื่น ๆ อีกมากมายและฉันพูดถึงบางส่วนของพวกเขาที่นี่ในบล็อกของฉัน: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html
ตัวอย่างสุดท้ายที่แสดงว่าการวางเวอร์ชันใน URL ไม่ดีอย่างไร ให้บอกว่าคุณต้องการข้อมูลบางส่วนภายในวัตถุและคุณได้กำหนดรุ่นวัตถุต่าง ๆ ของคุณ (ลูกค้าคือ v3.0 คำสั่งซื้อคือ v2.0 และวัตถุ shipto คือ v4.2) นี่คือ URL ที่น่ารังเกียจที่คุณต้องระบุในไคลเอนต์:
(Another reason why version in the URL sucks)
http://company.com/api/v3.0/customer/123/v2.0/orders/4321/
เราพบว่าใช้งานได้จริงและมีประโยชน์ในการวางเวอร์ชันใน URL มันทำให้ง่ายต่อการบอกสิ่งที่คุณกำลังใช้งานได้อย่างรวดเร็ว เราใช้ alias / foo to / foo / (เวอร์ชั่นล่าสุด) เพื่อความสะดวกในการใช้งาน URL ที่สั้นลง / ทำความสะอาด ฯลฯ ตามคำตอบที่ยอมรับ
การรักษาความเข้ากันได้ย้อนหลังตลอดไปมักจะเป็นต้นทุนที่ห้ามปรามและ / หรือยากมาก เราต้องการแจ้งให้ทราบล่วงหน้าเกี่ยวกับการคัดค้านการเปลี่ยนเส้นทางเช่นแนะนำที่นี่เอกสารและกลไกอื่น ๆ
ฉันยอมรับว่าการกำหนดเวอร์ชันการแสดงทรัพยากรดีกว่าเป็นไปตามแนวทาง REST ... แต่ปัญหาใหญ่อย่างหนึ่งของประเภท MIME ที่กำหนดเอง (หรือประเภท MIME ที่ต่อท้ายพารามิเตอร์รุ่น) เป็นการสนับสนุนที่ไม่ดีในการเขียนไปยังส่วนหัว Accept และ Content ใน HTML และ JavaScript
ตัวอย่างเช่นเป็นไปไม่ได้ที่ IMO จะ POST โดยมีส่วนหัวต่อไปนี้ในรูปแบบ HTML5 เพื่อสร้างทรัพยากร:
Accept: application/vnd.company.myapp-v3+json
Content-Type: application/vnd.company.myapp-v3+json
เพราะนี่คือ HTML5 enctype
แอตทริบิวต์การแจงนับดังนั้นอะไรอื่น ๆ กว่าปกติapplication/x-www-formurlencoded
, multipart/form-data
และtext/plain
ไม่ถูกต้อง
... และฉันไม่แน่ใจว่ารองรับเบราว์เซอร์ทั้งหมดใน HTML4 (ซึ่งมีแอตทริบิวต์ encaxpe lax มากกว่า แต่จะเป็นปัญหาการใช้งานเบราว์เซอร์ว่าประเภท MIME ถูกส่งต่อ)
ด้วยเหตุนี้ตอนนี้ฉันรู้สึกว่าวิธีที่เหมาะสมที่สุดสำหรับรุ่นคือผ่าน URI แต่ฉันยอมรับว่ามันไม่ใช่วิธีที่ 'ถูกต้อง'
วางเวอร์ชันของคุณใน URI API เวอร์ชันหนึ่งจะไม่สนับสนุนประเภทอื่นเสมอไปดังนั้นข้อโต้แย้งที่ว่าทรัพยากรที่ถูกโอนย้ายจากรุ่นหนึ่งไปยังอีกรุ่นหนึ่งนั้นผิดปกติเท่านั้น มันไม่เหมือนกับการเปลี่ยนรูปแบบจาก XML เป็น JSON ชนิดอาจไม่มีอยู่หรือมีการเปลี่ยนแปลงความหมาย
รุ่นเป็นส่วนหนึ่งของที่อยู่ทรัพยากร คุณกำลังกำหนดเส้นทางจาก API หนึ่งไปยังอีก API หนึ่ง ไม่เหลือที่จะซ่อนที่อยู่ในส่วนหัว
มีบางสถานที่ที่คุณสามารถทำการกำหนดเวอร์ชันใน REST API:
ตามที่ระบุไว้ใน URI สิ่งนี้สามารถจัดการได้ง่ายและน่าพึงพอใจหากการเปลี่ยนเส้นทางและสิ่งที่ชอบใช้งานได้ดี
ในส่วนหัว Accepts: ดังนั้นเวอร์ชันจะอยู่ในประเภทไฟล์ กดปุ่ม 'mp3' กับ 'mp4' สิ่งนี้จะใช้งานได้แม้ว่า IMO จะทำงานได้ดีกว่า ...
ในแหล่งทรัพยากรนั้นเอง รูปแบบไฟล์หลายรูปแบบมีหมายเลขรุ่นฝังอยู่ในส่วนหัว สิ่งนี้ทำให้ซอฟต์แวร์ใหม่สามารถ 'ทำงานได้' โดยการทำความเข้าใจกับไฟล์ประเภทที่มีอยู่ทั้งหมดในขณะที่ซอฟต์แวร์รุ่นเก่าสามารถถ่อหากมีการระบุรุ่นที่ไม่รองรับ (ใหม่กว่า) ในบริบทของ REST API หมายความว่า URIs ของคุณไม่จำเป็นต้องเปลี่ยนแปลงเพียงแค่การตอบสนองของคุณต่อข้อมูลรุ่นที่คุณส่งมา
ฉันเห็นเหตุผลที่ใช้ทั้งสามวิธี:
การกำหนดเวอร์ชัน REST API ของคุณนั้นคล้ายคลึงกับเวอร์ชันของ API อื่น ๆ การเปลี่ยนแปลงเล็กน้อยสามารถทำได้ในสถานที่การเปลี่ยนแปลงที่สำคัญอาจต้องใช้ API ใหม่ทั้งหมด วิธีที่ง่ายที่สุดสำหรับคุณคือเริ่มจากศูนย์ทุกครั้งซึ่งเมื่อใส่รุ่นใน URL จะสมเหตุสมผลที่สุด หากคุณต้องการทำให้ชีวิตง่ายขึ้นสำหรับไคลเอนต์ที่คุณพยายามรักษาความเข้ากันได้ย้อนหลังซึ่งคุณสามารถทำได้ด้วยการคัดค้าน (การเปลี่ยนเส้นทางถาวร) ทรัพยากรในหลาย ๆ รุ่นเป็นต้นซึ่งเป็นเรื่องที่ยุ่งยากมากขึ้นและต้องใช้ความพยายามมากขึ้น แต่ก็เป็นสิ่งที่ REST สนับสนุนใน "Cool URIs จะไม่เปลี่ยนแปลง"
ในท้ายที่สุดมันก็เหมือนกับการออกแบบ API อื่น ๆ ชั่งน้ำหนักความพยายามเพื่อความสะดวกของลูกค้า พิจารณาใช้การกำหนดเวอร์ชันเชิงความหมายสำหรับ API ของคุณซึ่งทำให้ชัดเจนสำหรับลูกค้าของคุณว่าเวอร์ชันใหม่ของคุณเข้ากันได้อย่างไร