Попытка разработать API для внешних приложений с предвидением изменений не легка, но немного продуманный заранее может облегчить жизнь в дальнейшем. Я пытаюсь создать схему, которая будет поддерживать будущие изменения, оставаясь обратно совместимой, оставляя на месте обработчики предыдущих версий.
Основная проблема в этой статье заключается в том, какой шаблон следует использовать для всех определенных конечных точек для данного продукта / компании.
Базовая Схема
Учитывая шаблон базового URL-адреса, https://rest.product.com/
я разработал, чтобы все службы находились /api
вместе с /auth
другими конечными точками, не основанными на отдыхе, такими как /doc
. Поэтому я могу установить базовые конечные точки следующим образом:
https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...
Конечные точки обслуживания
Теперь о самих конечных точках. Озабоченность POST
, GET
, DELETE
не является основной целью данной статьи и является заботой о самих этих действиях.
Конечные точки могут быть разбиты на пространства имен и действия. Каждое действие должно также представлять собой способ поддержки фундаментальных изменений в типе возвращаемого значения или обязательных параметрах.
Используя гипотетическую службу чата, где зарегистрированные пользователи могут отправлять сообщения, мы можем иметь следующие конечные точки:
https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send
Теперь добавим поддержку версий для будущих изменений API, которые могут быть неработоспособными. Мы могли бы добавить подпись версии после /api/
или после /messages/
. Учитывая send
конечную точку, мы могли бы иметь следующее для v1.
https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send
Итак, мой первый вопрос: какое место рекомендуется для идентификатора версии?
Управляющий код контроллера
Итак, теперь мы установили, что нам нужно поддерживать предыдущие версии, которые нам нужны, чтобы каким-то образом обрабатывать код для каждой из новых версий, который может со временем устаревать. Предполагая, что мы пишем конечные точки в Java, мы могли бы управлять этим через пакеты.
package com.product.messages.v1;
public interface MessageController {
void send();
Message[] list();
}
Преимущество этого заключается в том, что весь код был разделен через пространства имен, где любое критическое изменение будет означать, что новая копия конечных точек службы. Недостаток этого состоит в том, что весь код должен быть скопирован, и исправления ошибок, которые необходимо применить к новым, и предыдущие версии должны быть применены / протестированы для каждой копии.
Другой подход заключается в создании обработчиков для каждой конечной точки.
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;
}
}
}
}
Теперь это изолирует управление версиями для каждой конечной точки и делает совместимыми исправления ошибок с обратным портом, поскольку в большинстве случаев их нужно применять только один раз, но это означает, что нам нужно проделать довольно большую работу для каждой отдельной конечной точки, чтобы поддержать это.
Итак, мой второй вопрос: «Как лучше всего спроектировать код службы REST для поддержки предыдущих версий».