Cố gắng thiết kế API cho các ứng dụng bên ngoài với tầm nhìn xa để thay đổi là không dễ dàng, nhưng một chút suy nghĩ trước có thể giúp cuộc sống sau này dễ dàng hơn. Tôi đang cố gắng thiết lập một sơ đồ sẽ hỗ trợ các thay đổi trong tương lai trong khi vẫn tương thích ngược bằng cách đặt các trình xử lý phiên bản trước.
Mối quan tâm chính trong bài viết này là nên tuân theo mô hình nào cho tất cả các điểm cuối được xác định cho một sản phẩm / công ty nhất định.
Sơ đồ cơ sở
Đưa ra một mẫu URL cơ sở của https://rest.product.com/
tôi đã nghĩ ra rằng tất cả các dịch vụ nằm /api
cùng với /auth
và các điểm cuối không dựa trên phần còn lại khác, chẳng hạn như /doc
. Do đó tôi có thể thiết lập các điểm cuối cơ sở như sau:
https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...
Điểm cuối dịch vụ
Bây giờ cho các điểm cuối chính mình. Lo ngại về POST
, GET
, DELETE
không phải là mục tiêu chính của bài viết này và là mối quan tâm về những hành động của bản thân.
Điểm cuối có thể được chia thành các không gian tên và hành động. Mỗi hành động cũng phải thể hiện chính nó theo cách hỗ trợ các thay đổi cơ bản trong kiểu trả về hoặc các tham số bắt buộc.
Sử dụng dịch vụ trò chuyện giả định trong đó người dùng đã đăng ký có thể gửi tin nhắn, chúng tôi có thể có các điểm cuối sau:
https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send
Bây giờ để thêm hỗ trợ phiên bản cho các thay đổi API trong tương lai có thể bị phá vỡ. Chúng tôi có thể thêm chữ ký phiên bản sau /api/
hoặc sau /messages/
. Cho send
điểm cuối chúng ta có thể có đoạn sau cho v1.
https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send
Vì vậy, câu hỏi đầu tiên của tôi là, nơi được đề xuất cho định danh phiên bản là gì?
Quản lý mã điều khiển
Vì vậy, bây giờ chúng tôi đã thiết lập chúng tôi cần hỗ trợ các phiên bản trước mà chúng tôi cần để bằng cách nào đó xử lý mã cho từng phiên bản mới có thể không dùng nữa theo thời gian. Giả sử chúng ta đang viết các điểm cuối trong Java, chúng ta có thể quản lý điều này thông qua các gói.
package com.product.messages.v1;
public interface MessageController {
void send();
Message[] list();
}
Điều này có lợi thế là tất cả các mã đã được phân tách thông qua các không gian tên trong đó mọi thay đổi vi phạm sẽ có nghĩa là một bản sao mới của các điểm cuối dịch vụ. Bất lợi của điều này là tất cả các mã cần được sao chép và sửa lỗi mong muốn được áp dụng cho các phiên bản mới và trước đó cần được áp dụng / kiểm tra cho mỗi bản sao.
Một cách tiếp cận khác là tạo các trình xử lý cho mỗi điểm cuối.
package com.product.messages;
public class MessageServiceImpl {
public void send(String version) {
getMessageSender(version).send();
}
// Assume we have a List of senders in order of newest to oldest.
private MessageSender getMessageSender(String version) {
for (MessageSender s : senders) {
if (s.supportsVersion(version)) {
return s;
}
}
}
}
Điều này hiện cách ly phiên bản với từng điểm cuối và sửa lỗi cổng tương thích bởi trong hầu hết các trường hợp chỉ cần được áp dụng một lần, nhưng điều đó có nghĩa là chúng ta cần thực hiện thêm một chút công việc cho từng điểm cuối riêng lẻ để hỗ trợ điều này.
Vì vậy, có câu hỏi thứ hai của tôi "Cách tốt nhất để thiết kế mã dịch vụ REST để hỗ trợ các phiên bản trước."