Каков рекомендуемый шаблон для планирования конечных точек REST для прогнозируемых изменений

25

Попытка разработать 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 для поддержки предыдущих версий».

Бретт Райан
источник

Ответы:

13

Итак, мой второй вопрос: «Как лучше всего спроектировать код службы REST для поддержки предыдущих версий».

Очень тщательно спроектированный и ортогональный API, вероятно, никогда не придется менять несовместимыми способами, поэтому на самом деле лучший способ - не иметь будущих версий.

Конечно, вы, вероятно, не получите этого с первой попытки; Так:

  • Версия вашего API, как вы планируете (и это API, который является версионным, а не отдельные методы внутри), и шуметь об этом. Убедитесь, что ваши партнеры знают, что API может измениться, и что их приложения должны проверить, используют ли они последнюю версию; и посоветуйте пользователям обновить, когда появится более новый. Поддерживать две старые версии сложно, а поддерживать пять несостоятельно.
  • Не поддавайтесь желанию обновлять версию API с каждым «выпуском». Новые функции обычно можно перенести в текущую версию, не нарушая существующие клиенты; они новые функции. Последнее, что вы хотите, - это чтобы клиенты игнорировали номер версии, поскольку в любом случае он в основном обратно совместим. Обновляйте версию API только тогда, когда вы абсолютно не можете двигаться вперед, не нарушая существующий API.
  • Когда приходит время создавать новую версию, самым первым клиентом должна быть обратно-совместимая реализация предыдущей версии. «API обслуживания» должен быть реализован в текущем API. Таким образом, вы не готовы к тому, чтобы управлять несколькими полными реализациями; только текущая версия и несколько «оболочек» для старых версий. Выполнение регрессионного теста для ныне устаревшего API на клиенте с обратной совместимостью - это хороший способ проверить как новый API, так и уровень совместимости.
SingleNegationElimination
источник
3

Первый вариант разработки URI лучше отражает идею о том, что вы управляете версиями всего API. Второе можно интерпретировать как управление версиями самих сообщений. Так что лучше ИМО

rest.product.com/api/v1/messages/send

Для клиентской библиотеки я думаю, что использование двух полных реализаций в отдельных пакетах Java более чисто, проще в использовании и обслуживании.

Тем не менее, есть лучшие методы для развития API, чем управление версиями. Я согласен с вами, что вы должны быть готовы к этому, но я считаю версионирование методом последней инстанции, который следует использовать осторожно и экономно. Обновление клиентов требует относительно больших усилий. Некоторое время назад я поместил некоторые из этих мыслей в блоге:

http://theamiableapi.com/2011/10/18/api-design-best-practice-plan-for-evolution/

Специально для версий REST API вы найдете этот пост Марка Ноттингема полезным:

http://www.mnot.net/blog/2011/10/25/web_api_versioning_smackdown

Ференц Михай
источник
3

Другой подход к управлению версиями API заключается в использовании версии в заголовках HTTP. подобно

POST /messages/list/{user} HTTP/1.1
Host: http://rest.service.com
Content-Type: application/json
API-Version: 1.0      <----- like here
Cache-Control: no-cache

Вы можете проанализировать заголовок и обработать его соответствующим образом в бэкэнде.

При таком подходе клиентам не нужно менять URL, только заголовок. А также это делает конечные точки REST чище, всегда.

Если кто-либо из клиентов не отправил заголовок версии, вы либо отправляете запрос 400 - Bad, либо можете обработать его с помощью обратно-совместимой версии вашего API.

sincerekamal
источник