Thực tiễn tốt nhất cho phiên bản API? [đóng cửa]


877

Có bất kỳ cách thực hiện hoặc cách thực hành tốt nhất nào cho phiên bản API REST của dịch vụ web không?

Tôi đã nhận thấy rằng AWS thực hiện phiên bản bằng URL của điểm cuối . Đây có phải là cách duy nhất hoặc có những cách khác để thực hiện cùng một mục tiêu? Nếu có nhiều cách, giá trị của mỗi cách là gì?

Câu trả lời:


682

Đây là một câu hỏi hay và khó. Chủ đề của thiết kế URI đồng thời là phần nổi bật nhất của API REST và do đó, là một cam kết lâu dài có khả năng đối với người dùng API đó .

Do sự phát triển của một ứng dụng và ở mức độ thấp hơn, API của nó là một thực tế của cuộc sống và nó thậm chí giống với sự phát triển của một sản phẩm có vẻ phức tạp như ngôn ngữ lập trình, thiết kế URI nên có ít ràng buộc tự nhiên hơn và nó nên được bảo tồn tăng ca . Tuổi thọ của ứng dụng và API càng dài, cam kết với người dùng ứng dụng và API càng lớn.

Mặt khác, một thực tế khác của cuộc sống là khó có thể thấy trước tất cả các tài nguyên và các khía cạnh của chúng sẽ được tiêu thụ thông qua API. May mắn thay, không cần thiết phải thiết kế toàn bộ API sẽ được sử dụng cho đến tận thế . Nó là đủ để xác định chính xác tất cả các điểm cuối tài nguyên và sơ đồ địa chỉ của mọi thể hiện tài nguyên và tài nguyên.

Theo thời gian, bạn có thể cần thêm tài nguyên mới và thuộc tính mới cho từng tài nguyên cụ thể, nhưng phương thức mà người dùng API tuân theo để truy cập vào một tài nguyên cụ thể sẽ không thay đổi khi sơ đồ địa chỉ tài nguyên trở nên công khai và do đó cuối cùng.

Phương pháp này áp dụng cho ngữ nghĩa động từ HTTP (ví dụ PUT phải luôn cập nhật / thay thế) và mã trạng thái HTTP được hỗ trợ trong các phiên bản API trước đó (chúng nên tiếp tục hoạt động để các máy khách API hoạt động mà không cần sự can thiệp của con người có thể tiếp tục hoạt động. như thế).

Hơn nữa, vì việc nhúng phiên bản API vào URI sẽ phá vỡ khái niệm hypermedia như là công cụ của trạng thái ứng dụng (được nêu trong luận án tiến sĩ của Roy T. Fieldings) bằng cách có một địa chỉ tài nguyên / URI sẽ thay đổi theo thời gian, tôi sẽ kết luận rằng API các phiên bản không nên được giữ trong các URI tài nguyên trong một thời gian dài có nghĩa là các URI tài nguyên mà người dùng API có thể phụ thuộc phải là permalinks .

Chắc chắn, có thể nhúng phiên bản API trong URI cơ sở nhưng chỉ dành cho sử dụng hợp lý và bị hạn chế như gỡ lỗi ứng dụng khách API hoạt động với phiên bản API mới. Các API được phiên bản như vậy chỉ nên giới hạn thời gian và có sẵn cho các nhóm người dùng API bị giới hạn (như trong thời gian đóng betas). Nếu không, bạn cam kết chính mình nơi bạn không nên.

Một vài suy nghĩ về việc bảo trì các phiên bản API đã hết hạn sử dụng. Tất cả các nền tảng / ngôn ngữ lập trình thường được sử dụng để triển khai các dịch vụ web (Java, .NET, PHP, Perl, Rails, v.v.) cho phép dễ dàng liên kết (các) điểm cuối dịch vụ web với URI cơ sở. Bằng cách này, thật dễ dàng để thu thập và giữ một tập hợp các tệp / lớp / phương thức riêng biệt trên các phiên bản API khác nhau .

Từ người dùng API POV, việc làm việc và liên kết với một phiên bản API cụ thể cũng dễ dàng hơn khi điều này rõ ràng nhưng chỉ trong thời gian giới hạn, tức là trong quá trình phát triển.

Từ POV của người bảo trì API, việc duy trì song song các phiên bản API khác nhau bằng cách sử dụng các hệ thống kiểm soát nguồn chủ yếu hoạt động trên các tệp dưới dạng đơn vị nhỏ nhất của phiên bản (mã nguồn).

Tuy nhiên, với các phiên bản API hiển thị rõ ràng trong URI, có một cảnh báo: người ta cũng có thể phản đối cách tiếp cận này vì lịch sử API trở nên hiển thị / aparent trong thiết kế URI và do đó dễ bị thay đổi theo thời gian trái với hướng dẫn của REST. Tôi đồng ý!

Cách để giải quyết vấn đề phản đối hợp lý này là triển khai phiên bản API mới nhất theo URI cơ sở API không phiên bản. Trong trường hợp này, nhà phát triển ứng dụng khách API có thể chọn một trong hai:

  • phát triển dựa trên ứng dụng mới nhất (cam kết duy trì ứng dụng bảo vệ ứng dụng khỏi các thay đổi API cuối cùng có thể phá vỡ ứng dụng khách API được thiết kế xấu của chúng ).

  • liên kết với một phiên bản cụ thể của API (trở nên rõ ràng) nhưng chỉ trong một thời gian giới hạn

Ví dụ: nếu API v3.0 là phiên bản API mới nhất, hai cái sau sẽ là bí danh (nghĩa là hành xử giống hệt với tất cả các yêu cầu API):

http: // shonzilla / api / khách hàng / 1234 
http: // shonzilla / api /v3.0 / khách hàng / 1234
http: // shonzilla / api / v3 / khách hàng / 1234

Ngoài ra, các máy khách API vẫn cố gắng trỏ đến API nên được thông báo để sử dụng phiên bản API mới nhất trước đó, nếu phiên bản API chúng đang sử dụng bị lỗi thời hoặc không được hỗ trợ nữa . Vì vậy, truy cập vào bất kỳ URI lỗi thời nào như sau:

http: // shonzilla / api /v2.2 / khách hàng / 1234
http: // shonzilla / api /v2.0 / khách hàng / 1234
http: // shonzilla / api / v2 / khách hàng / 1234
http: // shonzilla / api /v1.1 / khách hàng / 1234
http: // shonzilla / api / v1 / khách hàng / 1234

sẽ trả về bất kỳ mã trạng thái HTTP 30x nào cho biết chuyển hướng được sử dụng cùng vớiLocation tiêu đề HTTP chuyển hướng đến phiên bản URI tài nguyên thích hợp vẫn là mã này:

http: // shonzilla / api / khách hàng / 1234

Có ít nhất hai mã trạng thái HTTP chuyển hướng phù hợp với các kịch bản phiên bản API:

  • 301 Đã di chuyển vĩnh viễn cho biết rằng tài nguyên có URI được yêu cầu được di chuyển vĩnh viễn sang một URI khác (phải là một permalink thể hiện tài nguyên không chứa thông tin phiên bản API). Mã trạng thái này có thể được sử dụng để chỉ ra phiên bản API lỗi thời / không được hỗ trợ, thông báo cho khách hàng API rằng URI tài nguyên được phiên bản đã được thay thế bằng permalink tài nguyên .

  • 302 Tìm thấy chỉ ra rằng tài nguyên được yêu cầu tạm thời được đặt tại một vị trí khác, trong khi URI được yêu cầu vẫn có thể được hỗ trợ. Mã trạng thái này có thể hữu ích khi các URI không có phiên bản tạm thời không khả dụng và yêu cầu phải được lặp lại bằng cách sử dụng địa chỉ chuyển hướng (ví dụ: chỉ vào URI với phiên bản APi được nhúng) và chúng tôi muốn nói với khách hàng tiếp tục sử dụng nó (ví dụ: permalinks).

  • các kịch bản khác có thể được tìm thấy trong chương Chuyển hướng 3xx của đặc tả HTTP 1.1


142
Sử dụng số phiên bản trong URL không nên được coi là một thực tiễn xấu khi triển khai cơ bản thay đổi. "Khi giao diện của dịch vụ thay đổi theo cách không tương thích ngược, thực tế, một dịch vụ hoàn toàn mới đã được tạo ra ... Từ quan điểm của khách hàng, một dịch vụ không khác gì giao diện và một số phẩm chất phi chức năng .. .nếu giao diện của dịch vụ thay đổi theo cách không tương thích ngược, nó không còn đại diện cho một thể hiện của dịch vụ ban đầu, mà là một dịch vụ hoàn toàn mới. " ibm.com/developerworks/webservice/l
Library / ws

7
Bạn có suy nghĩ gì về việc thêm một tiêu đề với số phiên bản để khách hàng hoặc nhà phát triển có thể kiểm tra nó không?
webclimber

11
Xem thêm việc sử dụng tiêu đề Chấp nhận để chỉ ra phiên bản mà khách hàng mong đợi: blog.steveklabnik.com/2011/07/03/ Kẻ
Weston Ruter

52
Đối với phần cuối cùng: Tôi sẽ nói rằng một API đã lỗi thời và không được hỗ trợ nữa sẽ quay trở lại 410 Gone, vì một chuyển hướng có thể chỉ ra rằng vị trí mới tương thích khi không. Nếu API chỉ đơn thuần là lỗi thời nhưng vẫn tồn tại, WarningTiêu đề HTTP trên Phản hồi có thể là một tùy chọn.
Michael Stum

22
Làm thế nào để bạn đối phó với các khách hàng đã sử dụng URL ổn định như shonzilla / api / khách hàng / 1234 và bạn muốn nâng cấp lên phiên bản mới? Làm thế nào bạn có thể buộc họ thêm V2 (cái cũ) vào URL?
Dejell

273

URL KHÔNG nên chứa các phiên bản. Phiên bản không liên quan gì đến "ý tưởng" về tài nguyên bạn đang yêu cầu. Bạn nên cố gắng nghĩ URL là một đường dẫn đến khái niệm bạn muốn - không phải là cách bạn muốn trả lại hàng. Phiên bản ra lệnh đại diện cho đối tượng, không phải khái niệm về đối tượng. Như các áp phích khác đã nói, bạn nên chỉ định định dạng (bao gồm cả phiên bản) trong tiêu đề yêu cầu.

Nếu bạn xem yêu cầu HTTP đầy đủ cho các URL có phiên bản, nó sẽ giống như sau:

(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>

Tiêu đề chứa dòng chứa đại diện bạn đang yêu cầu ("Chấp nhận: application / xml"). Đó là nơi phiên bản nên đi. Mọi người dường như che đậy sự thật rằng bạn có thể muốn điều tương tự ở các định dạng khác nhau và khách hàng sẽ có thể yêu cầu những gì họ muốn. Trong ví dụ trên, khách hàng đang yêu cầu BẤT K K đại diện tài nguyên XML nào - không thực sự là đại diện thực sự của những gì nó muốn. Về mặt lý thuyết, máy chủ có thể trả về một cái gì đó hoàn toàn không liên quan đến yêu cầu miễn là nó là XML và nó sẽ phải được phân tích cú pháp để nhận ra nó sai.

Một cách tốt hơn là:

(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>

Hơn nữa, giả sử các máy khách nghĩ rằng XML quá dài dòng và bây giờ họ muốn JSON thay thế. Trong các ví dụ khác, bạn sẽ phải có một URL mới cho cùng một khách hàng, vì vậy bạn sẽ kết thúc bằng:

(BAD)
http://company.com/api/JSONv3.0/customers/123
  or
http://company.com/api/v3.0/customers/123?format="JSON"

(hoặc một cái gì đó tương tự). Trong thực tế, mọi yêu cầu HTTP đều chứa định dạng bạn đang tìm kiếm:

(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"}
}

Sử dụng phương pháp này, bạn có nhiều tự do hơn trong thiết kế và thực sự tuân thủ ý tưởng ban đầu của REST. Bạn có thể thay đổi phiên bản mà không làm gián đoạn ứng dụng khách hoặc tăng dần khách hàng khi API được thay đổi. Nếu bạn chọn ngừng hỗ trợ một đại diện, bạn có thể trả lời các yêu cầu bằng mã trạng thái HTTP hoặc mã tùy chỉnh. Máy khách cũng có thể xác minh phản hồi theo đúng định dạng và xác thực XML.

Có nhiều lợi thế khác và tôi thảo luận về một số trong số chúng ở đây trên blog của tôi: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

Một ví dụ cuối cùng để cho thấy cách đưa phiên bản vào URL là xấu. Hãy nói rằng bạn muốn có một số thông tin bên trong đối tượng và bạn đã phiên bản các đối tượng khác nhau của mình (khách hàng là v3.0, đơn đặt hàng là v2.0 và đối tượng shipto là v4.2). Đây là URL khó chịu mà bạn phải cung cấp trong ứng dụng khách:

(Another reason why version in the URL sucks)
http://company.com/api/v3.0/customer/123/v2.0/orders/4321/

10
Xử lý phiên bản hợp đồng dữ liệu độc lập và phiên bản hợp đồng dịch vụ trong tiêu đề Chấp nhận có vẻ lộn xộn cũng như lộn xộn trong URL. Có sự lựa chọn nào khác không ? Ngoài ra nếu tôi có nhiều điểm cuối (xà phòng, phần còn lại), thì điều này cũng nên được chỉ định trong Chấp nhận và để dịch vụ định tuyến ở cuối máy chủ quyết định hướng đến điểm cuối chính xác HOẶC có thể chấp nhận điểm cuối được mã hóa trong URL không?
ideaf làng

117
Tôi không thể đồng ý với điều này, ít nhất là đến lý do cuối cùng của bạn. Điều này dường như đang nói rằng các phần khác nhau của URI có các phiên bản khác nhau. Nhưng đó không phải là điểm của phiên bản API. Vấn đề là có MỘT phiên bản cho tài nguyên ENTIRE. Nếu bạn thay đổi phiên bản, đó là một tài nguyên API khác. Đó là lý do tại sao không có ý nghĩa khi xem company.com/api/v3.0/customer/123/v2.0/nings/4321 mà là company.com/api/v3.0/customer/123/nings/4321 Bạn không phiên bản bất kỳ phần nào của tài nguyên, bạn đang phiên bản toàn bộ tài nguyên.
lừa4jesus

90
Về mặt ngữ nghĩa sử dụng số phiên bản trong tiêu đề có vẻ tốt hơn. Nhưng nó thực tế hơn nhiều khi sử dụng URL: ít bị lỗi hơn, được gỡ lỗi tốt nhất, dễ dàng được các nhà phát triển nhìn thấy, dễ dàng sửa đổi trong các máy khách thử nghiệm còn lại.
Daniel Cerecedo

7
Tôi nghĩ rằng BAD / TỐT trên đơn giản hóa câu hỏi. API là viết tắt của "Giao diện lập trình ứng dụng" và giao diện phiên bản dường như là một ý tưởng rất tốt. API không thực sự chỉ là về phục vụ tài nguyên. Điều cần phải tách biệt là một số người đang nói về giao diện và những người khác đang nói về tài nguyên. Nếu bạn nhìn kỹ vào api google maps trong tab mạng, bạn sẽ thấy rằng chúng bao gồm số phiên bản api trong url. Ví dụ: maps.google.com/maps/api/jsv2 trong khi xác thực. Số jsv2 là số api.
Tom Gruner

6
@Gili: Trên thực tế, bạn không nên sử dụng nữa -xvì nó không được RFC6648 phản đối .
Jonathan W

98

Chúng tôi thấy nó thực tế và hữu ích khi đưa phiên bản vào URL. Nó giúp bạn dễ dàng biết được những gì bạn đang sử dụng trong nháy mắt. Chúng tôi thực hiện bí danh / foo thành / foo / (phiên bản mới nhất) để dễ sử dụng, URL ngắn hơn / sạch hơn, v.v., như câu trả lời được chấp nhận cho thấy.

Giữ khả năng tương thích ngược mãi mãi thường rất tốn kém và / hoặc rất khó khăn. Chúng tôi muốn đưa ra thông báo nâng cao về sự phản đối, chuyển hướng như được đề xuất ở đây, tài liệu và các cơ chế khác.


5
Câu trả lời được chấp nhận có thể là chính xác và tinh khiết nhất. Tuy nhiên, đối với nhà phát triển và người dùng API hàng ngày, đây chắc chắn là cách dễ sử dụng và thiết lập nhất. Cách tiếp cận thực dụng nhất. Như được chỉ định bởi Google và Amazon khác cũng sử dụng phương pháp này.
Muhammad Rehan Saeed

46

Tôi đồng ý rằng việc phiên bản biểu diễn tài nguyên tốt hơn theo cách tiếp cận REST ... nhưng, một vấn đề lớn với các loại MIME tùy chỉnh (hoặc các loại MIME nối thêm tham số phiên bản) là hỗ trợ kém để ghi vào các tiêu đề Chấp nhận và Loại nội dung trong HTML và JavaScript.

Ví dụ: IMO không thể POST với các tiêu đề sau trong các biểu mẫu HTML5, để tạo tài nguyên:

Accept: application/vnd.company.myapp-v3+json
Content-Type: application/vnd.company.myapp-v3+json 

Điều này là do HTML5 enctypethuộc tính là một đếm, do đó bất cứ điều gì khác hơn so với thông thường application/x-www-formurlencoded, multipart/form-datatext/plainkhông hợp lệ.

... Tôi cũng không chắc là nó được hỗ trợ trên tất cả các trình duyệt trong HTML4 (có thuộc tính encytpe lỏng lẻo hơn, nhưng sẽ là vấn đề triển khai trình duyệt về việc loại MIME có được chuyển tiếp không)

Do đó, bây giờ tôi cảm thấy cách thích hợp nhất cho phiên bản là thông qua URI, nhưng tôi chấp nhận rằng đó không phải là cách 'chính xác'.


14
Giả sử tuyến đường được xác định phiên bản trong các tiêu đề, người ta có thể nói rằng các biểu mẫu HTML sử dụng đệ trình biểu mẫu gốc sẽ luôn sử dụng phiên bản API mới nhất vì chúng sẽ không vượt qua phiên bản cụ thể mà chúng muốn tuân thủ. Tuy nhiên, các yêu cầu XHR thực sự cho phép bạn thay đổi chấp nhận và đọc các tiêu đề loại nội dung. Vì vậy, các hình thức cơ bản thực sự là vấn đề duy nhất.
Kyle Hayes

Tôi không chắc chắn tôi đồng ý rằng URI là phù hợp nhất, nhưng thực tế là Kiểu nội dung không hoạt động với các biểu mẫu thực sự rất quan trọng.
wprl

2
@Kyle, tôi thấy một blog ở đâu đó nói rằng nếu bạn không chỉ định một phiên bản trong tiêu đề yêu cầu, tốt nhất là quay lại với phiên bản api đầu tiên không phải là phiên bản mới nhất để có khả năng tương thích tốt nhất.
andy

Điều đó thực sự có ý nghĩa với tôi bây giờ mà tôi nghĩ về.
Kyle Hayes

@KyleHayes đừng quên iframe, video / nhúng và các loại thẻ "src / href" khác.
pllee

21

Đặt phiên bản của bạn trong URI. Một phiên bản API sẽ không luôn luôn hỗ trợ các loại từ loại khác, do đó, lập luận rằng các tài nguyên chỉ được di chuyển từ phiên bản này sang phiên bản khác là hoàn toàn sai. Nó không giống như chuyển đổi định dạng từ XML sang JSON. Các loại có thể không tồn tại, hoặc chúng có thể đã thay đổi về mặt ngữ nghĩa.

Các phiên bản là một phần của địa chỉ tài nguyên. Bạn đang định tuyến từ API này sang API khác. Nó không RESTful để ẩn địa chỉ trong tiêu đề.


13

Có một vài nơi bạn có thể thực hiện phiên bản trong API REST:

  1. Như đã lưu ý, trong URI. Điều này có thể dễ dàng và thậm chí thẩm mỹ nếu chuyển hướng và tương tự được sử dụng tốt.

  2. Trong tiêu đề Ac accept:, vì vậy phiên bản nằm trong filetype. Giống như 'mp3' vs 'mp4'. Điều này cũng sẽ hoạt động, mặc dù IMO nó hoạt động kém độc đáo hơn một chút so với ...

  3. Trong chính tài nguyên. Nhiều định dạng tệp có số phiên bản được nhúng trong chúng, thường là trong tiêu đề; điều này cho phép phần mềm mới hơn 'chỉ hoạt động' bằng cách hiểu tất cả các phiên bản hiện có của filetype trong khi phần mềm cũ hơn có thể dừng nếu phiên bản không được hỗ trợ (mới hơn) được chỉ định. Trong ngữ cảnh của API REST, điều đó có nghĩa là các URI của bạn không bao giờ phải thay đổi, chỉ là phản hồi của bạn đối với phiên bản dữ liệu cụ thể mà bạn đã trao.

Tôi có thể thấy lý do để sử dụng cả ba cách tiếp cận:

  1. nếu bạn thích thực hiện các API mới 'quét sạch' hoặc cho các thay đổi phiên bản chính nơi bạn muốn có cách tiếp cận như vậy.
  2. nếu bạn muốn khách hàng biết trước khi nó thực hiện PUT / POST cho dù nó có hoạt động hay không.
  3. nếu nó ổn nếu khách hàng phải thực hiện PUT / POST để tìm hiểu xem nó có hoạt động không.

8

Phiên bản API REST của bạn tương tự như phiên bản của bất kỳ API nào khác. Những thay đổi nhỏ có thể được thực hiện tại chỗ, những thay đổi lớn có thể yêu cầu một API hoàn toàn mới. Cách dễ nhất cho bạn là bắt đầu lại từ đầu mọi lúc, đó là khi đưa phiên bản vào URL có ý nghĩa nhất. Nếu bạn muốn làm cho ứng dụng khách dễ dàng hơn, bạn cố gắng duy trì khả năng tương thích ngược, điều bạn có thể làm với sự phản đối (chuyển hướng vĩnh viễn), tài nguyên trong một số phiên bản, v.v. Điều này khó hơn và đòi hỏi nhiều nỗ lực hơn. Nhưng đó cũng là điều mà REST khuyến khích trong "URI tuyệt vời không thay đổi".

Cuối cùng, nó giống như mọi thiết kế API khác. Cân nhắc nỗ lực chống lại sự thuận tiện của khách hàng. Xem xét việc áp dụng phiên bản ngữ nghĩa cho API của bạn, điều này cho khách hàng thấy rõ phiên bản mới của bạn tương thích ngược như thế nào.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.