Tóm tắt
Phiên bản hóa URL (/v1/pets) là chiến lược phiên bản hóa API thực tế nhất cho hầu hết các nhóm. Nó dễ nhìn thấy, có thể lưu vào bộ nhớ cache và dễ kiểm thử. Phiên bản hóa qua tiêu đề và đàm phán nội dung "thuần" REST hơn nhưng làm tăng độ phức tạp. Modern PetstoreAPI sử dụng phiên bản hóa URL với phiên bản hóa ngữ nghĩa và các chính sách ngừng hỗ trợ rõ ràng.
Giới thiệu
API của bạn cần một thay đổi gây hỏng. Bạn đang thay đổi định dạng phản hồi cho /pets từ một mảng trần sang một đối tượng bao bọc với siêu dữ liệu phân trang. Các client hiện có sẽ bị hỏng. Bạn sẽ làm gì?
Bạn cần phiên bản hóa API. Nhưng chiến lược nào? Phiên bản hóa URL (/v1/pets so với /v2/pets)? Phiên bản hóa qua tiêu đề (Accept: application/vnd.petstore.v1+json)? Đàm phán nội dung? Mỗi cách tiếp cận đều có những người ủng hộ nhiệt tình và những quan điểm mạnh mẽ.
Câu trả lời: Phiên bản hóa URL thắng thế đối với hầu hết các nhóm. Nó thực dụng, dễ nhìn thấy và hoạt động với tất cả các công cụ HTTP. Phiên bản hóa qua tiêu đề và đàm phán nội dung về mặt lý thuyết là sạch hơn nhưng làm tăng độ phức tạp mà hầu hết các nhóm không cần.
Modern PetstoreAPI sử dụng phiên bản hóa URL với phiên bản hóa ngữ nghĩa và các chính sách ngừng hỗ trợ rõ ràng. Phiên bản hiện tại là v1, với v2 được lên kế hoạch cho các thay đổi gây hỏng trong tương lai.
Trong hướng dẫn này, bạn sẽ tìm hiểu ba chiến lược phiên bản hóa chính, những đánh đổi của chúng và cách triển khai phiên bản hóa một cách chính xác bằng cách sử dụng Modern PetstoreAPI làm tài liệu tham khảo.
Tại sao API cần phiên bản hóa
API phát triển. Bạn thêm tính năng, sửa lỗi và cải thiện thiết kế. Đôi khi những thay đổi này làm hỏng các client hiện có.
Những thay đổi gây hỏng (Breaking Changes)
Những thay đổi làm hỏng các client hiện có:
1. Xóa trường:
// v1
{"id": "123", "name": "Fluffy", "age": 3}
// v2 (gây hỏng: đã xóa age)
{"id": "123", "name": "Fluffy"}
2. Thay đổi kiểu trường:
// v1
{"price": "19.99"}
// v2 (gây hỏng: chuỗi sang số)
{"price": 19.99}
3. Thay đổi cấu trúc phản hồi:
// v1 (mảng trần)
[{"id": "123"}]
// v2 (gây hỏng: đối tượng bao bọc)
{"data": [{"id": "123"}], "pagination": {...}}
4. Thay đổi cấu trúc URL:
// v1
GET /pet/123
// v2 (gây hỏng: số nhiều)
GET /pets/123
5. Thay đổi xác thực:
// v1: API key trong truy vấn
GET /pets?api_key=xxx
// v2 (gây hỏng: mã thông báo Bearer)
GET /pets
Authorization: Bearer xxx
Những thay đổi không gây hỏng (Non-Breaking Changes)
Những thay đổi không làm hỏng client:
- Thêm các endpoint mới
- Thêm các trường tùy chọn vào yêu cầu
- Thêm các trường mới vào phản hồi (các client nên bỏ qua các trường không xác định)
- Thêm các tham số truy vấn mới
- Thêm các phương thức HTTP mới vào tài nguyên hiện có
Quyết định phiên bản hóa
Khi bạn cần một thay đổi gây hỏng, bạn có hai lựa chọn:
1. Buộc tất cả các client phải nâng cấp - Đơn giản nhưng làm hỏng các tích hợp hiện có
2. Hỗ trợ nhiều phiên bản - Tốn nhiều công sức hơn nhưng duy trì khả năng tương thích ngược
Hầu hết các API công khai chọn tùy chọn 2. Phiên bản hóa cho phép bạn phát triển API trong khi vẫn cho client thời gian để di chuyển.
Phiên bản hóa URL
Phiên bản hóa URL đặt số phiên bản vào đường dẫn URL.
Cách hoạt động
GET /v1/pets
GET /v2/pets
Phiên bản là một phần của định danh tài nguyên. Các phiên bản khác nhau là các tài nguyên khác nhau.
Ưu điểm
1. Rõ ràng và tường minh
Phiên bản nằm trong URL. Bạn có thể thấy nó trong nhật ký, lịch sử trình duyệt và tài liệu. Không cần phải nhớ các tiêu đề ẩn.
2. Dễ kiểm thử
curl https://petstoreapi.com/v1/pets
curl https://petstoreapi.com/v2/pets
Bạn có thể kiểm thử cả hai phiên bản bằng các yêu cầu HTTP đơn giản.
3. Hoạt động với tất cả các công cụ HTTP
Trình duyệt, bộ nhớ cache, proxy và bộ cân bằng tải xem các URL khác nhau. Chúng có thể định tuyến, lưu vào bộ nhớ cache và ghi nhật ký từng phiên bản một cách độc lập.
4. Đơn giản cho client
Các client chỉ cần thay đổi URL. Không có tiêu đề tùy chỉnh hoặc logic đàm phán nội dung.
5. Dễ dàng ngừng hỗ trợ
Bạn có thể loại bỏ các endpoint /v1 mà không ảnh hưởng đến /v2.
Nhược điểm
1. Không "thuần" REST
Những người theo chủ nghĩa thuần túy REST lập luận rằng /v1/pets/123 và /v2/pets/123 là cùng một tài nguyên, vì vậy chúng nên có cùng URL. Phiên bản nên nằm trong tiêu đề hoặc đàm phán nội dung.
2. Ô nhiễm URL
API của bạn có nhiều không gian URL: /v1/*, /v2/*, v.v.
3. Khó phiên bản hóa các tài nguyên riêng lẻ hơn
Nếu bạn muốn phiên bản hóa chỉ một endpoint, bạn cần phiên bản hóa toàn bộ API hoặc tạo ra sự không nhất quán.
Triển khai
Phiên bản chính trong URL:
/v1/pets
/v2/pets
Không bao gồm các phiên bản nhỏ:
❌ /v1.2/pets (quá chi tiết)
✅ /v1/pets (chỉ phiên bản chính)
Sử dụng phiên bản hóa ngữ nghĩa nội bộ:
- v1.0.0 - Phát hành ban đầu
- v1.1.0 - Thêm trường mới (không gây hỏng)
- v1.2.0 - Thêm các endpoint mới (không gây hỏng)
- v2.0.0 - Thay đổi gây hỏng (URL mới: /v2)
Modern PetstoreAPI sử dụng phiên bản hóa URL với /v1 là phiên bản hiện tại.
Phiên bản hóa qua tiêu đề
Phiên bản hóa qua tiêu đề đặt số phiên bản vào một tiêu đề HTTP tùy chỉnh.
Cách hoạt động
GET /pets
API-Version: 1
GET /pets
API-Version: 2
URL giữ nguyên. Tiêu đề chỉ định phiên bản.
Ưu điểm
1. URL sạch
/pets giống nhau cho tất cả các phiên bản. Không có tiền tố /v1 hoặc /v2.
2. "RESTful" hơn
Định danh tài nguyên (/pets/123) không thay đổi. Biểu diễn thay đổi dựa trên tiêu đề.
3. Phiên bản hóa chi tiết
Bạn có thể phiên bản hóa các tài nguyên riêng lẻ:
GET /pets
API-Version: 2
GET /orders
API-Version: 1
Nhược điểm
1. Không hiển thị
Phiên bản không nằm trong URL. Bạn không thể thấy nó trong nhật ký hoặc lịch sử trình duyệt nếu không kiểm tra tiêu đề.
2. Khó kiểm thử hơn
curl -H "API-Version: 1" https://petstoreapi.com/pets
curl -H "API-Version: 2" https://petstoreapi.com/pets
Bạn phải nhớ bao gồm tiêu đề.
3. Độ phức tạp bộ nhớ cache
Bộ nhớ cache phải xem xét tiêu đề API-Version. Bạn cần Vary: API-Version trong các phản hồi.
4. Độ phức tạp của client
Các client cần logic tiêu đề tùy chỉnh. Không phải tất cả các client HTTP đều làm điều này dễ dàng.
5. Sự mơ hồ của phiên bản mặc định
Điều gì xảy ra nếu client không gửi tiêu đề? Bạn cần một giá trị mặc định, điều này tạo ra hành vi ngầm định.
Triển khai
Tiêu đề tùy chỉnh:
API-Version: 1
Hoặc sử dụng tiêu đề Accept:
Accept: application/vnd.petstore.v1+json
Bao gồm tiêu đề Vary:
Vary: API-Version
Điều này cho bộ nhớ cache biết phải xem xét tiêu đề khi lưu vào bộ nhớ cache.
Đàm phán nội dung
Đàm phán nội dung sử dụng tiêu đề Accept với các kiểu phương tiện tùy chỉnh.
Cách hoạt động
GET /pets
Accept: application/vnd.petstore.v1+json
GET /pets
Accept: application/vnd.petstore.v2+json
Phiên bản là một phần của kiểu phương tiện.
Ưu điểm
1. "RESTful" nhất
Đây là cách REST được thiết kế. Các biểu diễn khác nhau của cùng một tài nguyên.
2. Tuân thủ các tiêu chuẩn HTTP
Sử dụng đàm phán nội dung HTTP tiêu chuẩn.
3. Hỗ trợ nhiều định dạng
Bạn có thể phiên bản hóa và định dạng đồng thời:
Accept: application/vnd.petstore.v1+json
Accept: application/vnd.petstore.v1+xml
Nhược điểm
1. Phức tạp
Các client phải hiểu các kiểu phương tiện và đàm phán nội dung.
2. Khó kiểm thử hơn
curl -H "Accept: application/vnd.petstore.v1+json" https://petstoreapi.com/pets
3. Hỗ trợ công cụ kém
Nhiều client và công cụ HTTP không xử lý tốt các kiểu phương tiện tùy chỉnh.
4. Độ phức tạp bộ nhớ cache
Bộ nhớ cache phải xem xét tiêu đề Accept. Bạn cần Vary: Accept.
5. Không cần thiết đối với hầu hết các API
Hầu hết các API không cần mức độ tinh vi này.
Triển khai
Kiểu phương tiện dành riêng cho nhà cung cấp:
Accept: application/vnd.petstore.v1+json
Phản hồi:
Content-Type: application/vnd.petstore.v1+json
Vary: Accept
Cách Modern PetstoreAPI triển khai phiên bản hóa
Modern PetstoreAPI sử dụng phiên bản hóa URL với các chính sách rõ ràng.
Phiên bản hiện tại: v1
https://petstoreapi.com/v1/pets
https://petstoreapi.com/v1/orders
https://petstoreapi.com/v1/users
Tất cả các endpoint đều nằm dưới /v1.
Tiêu đề phản hồi phiên bản
Mỗi phản hồi đều bao gồm phiên bản API:
X-API-Version: 1.2.0
Điều này hiển thị phiên bản chính xác (major.minor.patch) mặc dù URL chỉ hiển thị phiên bản chính.
Cảnh báo ngừng hỗ trợ
Khi một phiên bản bị ngừng hỗ trợ, các phản hồi bao gồm:
Deprecation: true
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Link: <https://docs.petstoreapi.com/migration/v1-to-v2>; rel="deprecation"
Deprecation- Chỉ ra phiên bản đã bị ngừng hỗ trợSunset- Thời điểm phiên bản sẽ bị gỡ bỏLink- Hướng dẫn di chuyển
Khám phá phiên bản
Endpoint gốc liệt kê các phiên bản có sẵn:
GET https://petstoreapi.com/
{
"versions": [
{
"version": "v1",
"status": "current",
"docsUrl": "https://docs.petstoreapi.com/v1"
}
]
}
Phiên bản hóa ngữ nghĩa
Modern PetstoreAPI tuân theo phiên bản hóa ngữ nghĩa nội bộ:
- Chính (v1, v2) - Thay đổi gây hỏng, URL mới
- Nhỏ (v1.1, v1.2) - Tính năng mới, tương thích ngược
- Sửa lỗi (v1.1.1, v1.1.2) - Sửa lỗi, tương thích ngược
Chỉ các phiên bản chính xuất hiện trong URL.
Kiểm thử các phiên bản API với Apidog
Apidog giúp bạn kiểm thử nhiều phiên bản API.
Nhập nhiều phiên bản
Nhập các thông số kỹ thuật OpenAPI cho mỗi phiên bản:
petstore-v1.yaml → Environment: v1
petstore-v2.yaml → Environment: v2
Chạy kiểm thử trên tất cả các phiên bản
Tạo các bộ kiểm thử chạy trên cả hai phiên bản:
// Kiểm thử v1
pm.environment.set("baseUrl", "https://petstoreapi.com/v1");
pm.sendRequest(pm.environment.get("baseUrl") + "/pets");
// Kiểm thử v2
pm.environment.set("baseUrl", "https://petstoreapi.com/v2");
pm.sendRequest(pm.environment.get("baseUrl") + "/pets");
Xác thực hành vi cụ thể theo phiên bản
Kiểm thử v1 và v2 hoạt động khác nhau:
// v1 trả về mảng trần
pm.test("v1 returns array", function() {
pm.expect(pm.response.json()).to.be.an('array');
});
// v2 trả về đối tượng bao bọc
pm.test("v2 returns wrapped object", function() {
pm.expect(pm.response.json()).to.have.property('data');
pm.expect(pm.response.json()).to.have.property('pagination');
});
Kiểm tra tiêu đề ngừng hỗ trợ
Kiểm thử các phiên bản đã ngừng hỗ trợ bao gồm các tiêu đề thích hợp:
pm.test("Deprecated version includes headers", function() {
pm.response.to.have.header("Deprecation");
pm.response.to.have.header("Sunset");
});
Chiến lược ngừng hỗ trợ phiên bản
Cách ngừng hỗ trợ các phiên bản cũ mà không làm hỏng các client.
1. Thông báo ngừng hỗ trợ sớm
Thông báo cho client ít nhất 6-12 tháng:
Deprecation: true
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
2. Cung cấp hướng dẫn di chuyển
Tài liệu hóa tất cả các thay đổi gây hỏng và cách di chuyển:
Link: <https://docs.petstoreapi.com/migration/v1-to-v2>; rel="deprecation"
3. Giám sát việc sử dụng
Theo dõi những client nào vẫn sử dụng các phiên bản đã ngừng hỗ trợ:
X-API-Version: 1.2.0
X-Client-ID: abc123
Liên hệ trực tiếp với client nếu cần.
4. Tắt dần
Không gỡ bỏ phiên bản ngay lập tức:
- Tháng 1-6: Thông báo ngừng hỗ trợ
- Tháng 7-9: Thêm tiêu đề ngừng hỗ trợ
- Tháng 10-11: Giảm giới hạn tốc độ cho phiên bản đã ngừng hỗ trợ
- Tháng 12: Gỡ bỏ phiên bản đã ngừng hỗ trợ
5. Giữ lại tài liệu
Ngay cả sau khi gỡ bỏ, hãy giữ lại tài liệu cho phiên bản cũ. Client có thể cần tham khảo nó.
Kết luận
Phiên bản hóa URL là chiến lược phiên bản hóa API thực tế nhất cho hầu hết các nhóm. Nó dễ nhìn thấy, dễ kiểm thử và hoạt động với tất cả các công cụ HTTP. Phiên bản hóa qua tiêu đề và đàm phán nội dung "thuần" REST hơn nhưng làm tăng độ phức tạp.
Modern PetstoreAPI sử dụng phiên bản hóa URL với /v1 là phiên bản hiện tại, phiên bản hóa ngữ nghĩa nội bộ và các chính sách ngừng hỗ trợ rõ ràng. Cách tiếp cận này cân bằng giữa tính thực dụng và thiết kế API tốt.
Sử dụng Apidog để kiểm thử nhiều phiên bản API, xác thực hành vi cụ thể theo phiên bản và đảm bảo quá trình di chuyển mượt mà giữa các phiên bản.
Câu hỏi thường gặp
Tôi nên sử dụng phiên bản hóa URL hay phiên bản hóa tiêu đề?
Hãy sử dụng phiên bản hóa URL trừ khi bạn có lý do cụ thể để không làm vậy. Nó đơn giản hơn, dễ nhìn thấy hơn và dễ kiểm thử hơn. Phiên bản hóa qua tiêu đề "RESTful" hơn nhưng làm tăng độ phức tạp mà hầu hết các nhóm không cần.
Tôi nên hỗ trợ bao nhiêu phiên bản đồng thời?
Hỗ trợ tối đa 2 phiên bản: hiện tại và trước đó. Hỗ trợ nhiều hơn sẽ tạo gánh nặng bảo trì. Cho client 6-12 tháng để di chuyển, sau đó gỡ bỏ các phiên bản cũ.
Tôi nên bắt đầu phiên bản từ v0 hay v1?
Bắt đầu với v1. v0 ngụ ý sự không ổn định. Nếu API của bạn chưa đủ ổn định cho v1, đừng phát hành công khai.
Tôi có cần phiên bản hóa mọi endpoint không?
Không. Chỉ phiên bản hóa khi bạn thực hiện các thay đổi gây hỏng. Nếu bạn thêm các endpoint mới mà không thay đổi các endpoint hiện có, bạn không cần một phiên bản mới.
Còn các phiên bản nhỏ trong URL thì sao?
Không bao gồm các phiên bản nhỏ trong URL. Sử dụng /v1, không phải /v1.2. Các phiên bản nhỏ tương thích ngược, vì vậy các client không cần thay đổi URL.
Làm thế nào để xử lý các lỗi cụ thể theo phiên bản?
Sửa lỗi trong tất cả các phiên bản được hỗ trợ. Nếu một lỗi chỉ tồn tại trong v1, hãy sửa nó trong v1. Đừng buộc các client phải nâng cấp lên v2 để sửa lỗi.
Tôi có nên sử dụng phiên bản hóa ngữ nghĩa không?
Có, nội bộ. Theo dõi các phiên bản major.minor.patch, nhưng chỉ hiển thị các phiên bản chính trong URL. Điều này mang lại cho bạn sự linh hoạt cho các thay đổi không gây hỏng.
Điều gì nếu tôi chỉ cần phiên bản hóa một endpoint?
Với phiên bản hóa URL, bạn sẽ cần phiên bản hóa toàn bộ API hoặc tạo ra sự không nhất quán. Đây là một sự đánh đổi. Hầu hết các nhóm chấp nhận phiên bản hóa toàn bộ API để đơn giản.
