Делаем новую версию API. Быстро и легко

    Коммуникация правит миром. Взаимодействие необходимо и между людьми, и между программным обеспечением. Хотите адекватного ответа на ваш запрос к приложению?  API вам в помощь! Необходимость в реализации API возникает практически во всех проектах, и со временем мы задумываемся, можно ли улучшить текущий API? Последовательность конкретных шагов и реальные примеры – наш рецепт создания рабочего web-API проекта.

    Первый вопрос, который нужно задать: «А точно ли стоит реализовать новую версию API?» Возможно, уже работающая версия отвечает всем необходимым вам критериям. Но если вы уже для себя ответили «да» на поставленный вопрос, то читайте дальше и найдете наш ответ. Если вы ответили «нет», то тоже читайте дальше и применяйте наш опыт проектирования и реализации в следующих ваших проектах.

    Итак, какие же задачи могут стать поводом к разработке новой реализации API. В рамках развития программных продуктов, например, в сфере комплексной безопасности, как у нас, периодически появляется необходимость ускорить процесс клиентской и серверной̆ разработки, или снизить временные затраты на поддержку и развитие API, или упростить поддержку обратной совместимости с помощью версионности API, а также добавить автогенерацию документации API. Или все эти задачи одновременно.

    Какие шаги можно предпринять для решения этих задач, и как выйти на новый уровень зрелости API можно узнать в этой статье. Реальный алгоритм действий при разработке новой реализации API, описанный здесь, поможет в создании собственного проекта, а возможно станет предметом дискуссии. Комментарии профессионалов приветствуются!

    Рассмотрим на примере API для работы с котиками то, как мы совершенствовали один из наших проектов.

    Как понять, что стоит реализовать новую версию API

    Для того чтобы понять, что API пора спасать, необходимо пройтись по следующим пунктам:

    • определить уровень зрелости API;

    • проверить, есть ли у API версионность;

    • проверить наличие документации API.

    Разберем каждый пункт по отдельности, чтобы точно определиться, стоит ли «игра свеч» или «и так сойдет».

    Определим уровень зрелости API

    Для этого идеально подходит модель Леонарда Ричардсона, в которой он выделяет четыре уровня зрелости API:

    • Уровень 0: Один URI и один HTTP метод (в основном метод POST);

    • Уровень 1: Несколько URI и один HTTP метод;

    • Уровень 2: Несколько URI, каждый из которых поддерживает разные HTTP методы;

    • Уровень 3: HATEOAS. Ресурсы сами описывают свои возможности и взаимосвязи.

    Если API соответствует 0 или 1 уровню зрелости, то определенно есть куда расти, потому что:

    • Если используется один URI, то не понятно с каким ресурсом работаем;

    • Не понятно, что делаем с ресурсом, так как используется один HTTP метод;

    • Использование одного URI создает трудности с документацией̆ API;

    • Использование одного URI создает трудности с логированием входящих запросов;

    • Из-за использования одного URI, информация о типе ресурса передается в теле запроса.

    Посчитаем, сколько «если» у нас получилось, и определим направление разработки. Это поможет сконцентрироваться на задачах и четко сформулировать дальнейшие шаги.

    Проверим, есть ли у API версионность

    Затем необходимо проверить версионность. Это позволит реализовывать изменения в текущем API, а при необходимости даст возможность включать расширения в новую версию API. Кроме того, она позволит серверу облегчить обеспечение обратной совместимости.

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

    Рассмотрим 3 основных способа версионирования API и разберем подробнее каждый из них. Современные разработчики выделяют следующие способы:

    • Использование разных URI (Uniform Resource Identifier);

    • Использование параметра запроса;

    • Использование заголовка, Accept Header/Media Type.

    Каждый из приведенных способов версионирования имеет свои плюсы и минусы. И для каждого конкретного случая реализации API необходимо оценить и выбрать оптимальный способ или их комбинацию.

    1. Использование разных URI простой в проектировании, реализации и документировании способ версионирования. Однако он имеет целый ряд недостатков:

    • приводит к загрязнению URI, так как префиксы и суффиксы добавляются к основным строкам URI;

    • разбивает существующие URI, то есть все клиенты должны обновиться до нового;

    • приводит к увеличению размер HTTP кэша для хранения нескольких версий;

    • создает большое количество дубликатов URI, может снизить производительность приложения из-за увеличения количества обращений к кэшу;

    • является крайне негибким и не позволяет просто изменить ресурс или небольшой их набор.

    Пример:

    GET  v1/cats/{name}

    2. Использование параметра запроса позволяет легко документировать версионность и рекомендуется к использованию в случае, если важно HTTP – кэширование. Однако и этот способ приводит к загрязнению пространства URI.

    Пример:

    GET  cats/{name}?version=v1

    3.    Использование заголовка и Accept Header/Media Type также легко документировать и, в отличие от предыдущих способов не приводит к загрязнению пространства URI. Но и у этого способа выделяют несколько минусов:

    • приводит к неправильному использованию заголовков, так как они изначально предусматривались не для версионирования;

    • требует для управления версиями на основе заголовка и типа медиа использования таких инструментов, как Postman, или создания автоматизированных средств, добавляющих необходимый̆ заголовок в HTTP запрос.

    Пример:

    GET  cats/{name}
    Headers:  version=v1

    Выбор конкретного способа целиком лежит на плечах разработчика, исходя из поставленной задачи и навыков. Каждый вариант вполне рабочий, но имеет свою специфику и особенности реализации.

    Проверим наличие документации API

    Даже к фену удобно иметь описание, не говоря уже о серьезной проекте разработки ПО. Поэтому наглядное описание API всегда удобно для использования как backend, так и frontend разработчиками. Документация может быть реализована, например, с помощью Swagger (фреймворк для спецификации RESTful API), он дает возможность не только интерактивно просматривать спецификацию, но и отправлять запросы с помощью Swagger UI:

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

    • соответствует 0 уровню зрелости (Один URI и один HTTP метод);

    • невозможно достоверно установить, с каким ресурсом API работает и какие функции выполняет;

    • отсутствуют автоматизированные средства документации API, что приводит к неполной документации API или ее полному отсутствию для некоторых запросов;

    • появляются сложности с поддержкой обратной совместимости, так как нет версионности API.

    Для большего понимания того, что не нравилось, приведем пример того, как выглядел наш API.

     Пример:

    POST /cats       -   должен вернуть котика по имени Пушок ( гарантируется, 
    что у          
    requestBody: {        котиков уникальные имена);
      "name": "Pushok"
    }
    
    POST /cats -   должен вернуть вернуть список  белых котиков;        
    requestBody: {                                                                   
      "color": "white"
    }
    

    Цели реализации нового API

    Если в предыдущем пункте мы уверенно выявили хотя бы один пункт в существующем API, то самое время переходить к реализации нового. Основными целями, которые преследует разработчики при создании API являются:

    • Ускорение процесса клиентской и серверной̆ разработки;

    • Снижение временных затрат на поддержку и развитие API;

    • Добавление автогенерации документации API;

    • Поддержка версионности API для упрощения поддержки обратной совместимости с помощью версионности API.

    Таким образом, проект получает автономность от его создателя, гибкость в масштабировании и апгрейде и, зачастую, серьезно выигрывает по производительности в целом.

    Повышаем уровень зрелости API

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

    Для того, чтобы повысить API с 0 на 2 уровень зрелости были проанализированы все текущие проблемы и выделены следующие пункты:

     1. Разделение текущего API на смысловые части для выделения соответствующего ресурса для каждой части;

    2. Использование методов, соответствующих действиям над ресурсами: GET, POST, PUT, DELETE;

    3. Обозначение ресурса во множественном числе;

     Пример:

    GET /cats - должен вернуть список котиков
    GET /cats/Pushok - должен вернуть котика по имени Пушок
                       (гарантируется, что у котиков уникальные имена)

     4.    Указание фильтрации в параметрах.

    Пример:

    GET /cats?color=white - должен вернуть список белых котиков

    Добавляем версионность

    После повышения зрелости API выходим на новый этап и выбираем способ версионирования. Для текущего проекта был выбран способ версионирования с использованием собственного заголовка. Это решение не попадает в пункт “неправильное использование заголовков”, так как будет использоваться собственный заголовок. Для удобства было решено указывать версии вида “2.n”.

     Для начала реализуем контроллер:

    После этого для реализации версионности, создадим enum:

    Далее создадим конвертер, используя внутренний Spring Framework интерфейс Converter<S,T>. Он преобразует исходный объект типа S в целевой типа T, в нашем случае преобразует текстовое значение версии в тип Enum ApiVersion до попадания в контроллер:

    Если в заголовке было отправлено значение “2.0”, до контроллера оно дойдет уже в виде “v2”. При такой реализации вместо того, чтобы перечислять все доступные версии, можно будет указать в заголовке, что ожидается enum. А контроллер после добавления версионирования будет выглядеть так:

    При дальнейшем развитии API и реализации новых версий, список всех существующих версий будет храниться в enum. Когда для конкретной̆ новой̆ версии потребуется уникальная реализация, то в контроллере можно будет указать версию явно.

    Указание версии было решено сделать обязательным.

    Документируем

    И наконец переходим к документированию. Критерии выбора конкретного инструмента были основаны на собственном опыте и дискуссии с коллегами-разработчиками. В ходе обсуждения был выбран популярный и широко используемый инструмент автогенерации API Swagger. Он не является единственным для решения задач документирования, но в нашем случае был признан оптимальным, так как бесплатен, прост для освоения и обладает необходимым набором функций для грамотного документирования API.

    Рассмотрим пример реализации документации для созданного выше контроллера. Для этого реализуем интерфейс:

    С его помощью можно добавить название и описание API текущего контроллера, описание каждого запроса, плюс есть возможность указать схему тела запроса. Остальную информацию Swagger получает из аннотаций контроллера.

    Перейдя в Swagger UI увидим задокументированное API:

    Получим более подробную информацию:

    Преимущества новой версии API

    На примерах выше был продемонстрирован переход с 0 уровня зрелости API на 2 уровень зрелости согласно модели Ричардсона, благодаря этому мы получили:

    • повышение уровня зрелости API, что дало понимание, с каким ресурсом мы работаем и что с ним делаем;

    • версионирование, которое позволит вносить изменения в текущий API;

    • удобное документирование и появление внятной документации, да и документации вообще.

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

    Скорость разработки при переходе на новую версию API значительно возросла, ведь разработчикам стало значительно удобнее и приятнее работать. Наш путь разработки оказался вполне рабочим и его смело можно рекомендовать для применения в реальных проектах. А может в вашем арсенале есть другие варианты? Готовы обсудить!

    НТЦ ПРОТЕЙ
    Компания

    Комментарии 2

      0

      Только пользы от предложенной метрики "зрелости" апи нету.
      Во первых, то что там происходит в url не важно, во вторых введение "ресурсов" в текущем виде похоже на моделирование через CRUD, что хорошо далеко не всегда, а подаётся как обновление ради обновления.
      И да, версионирование апи — вынужденная неприятность.

        0
        Приведенный пример улучшения апи не является единственно возможным и он не на все случаи жизни.

        Предложенные в статье метрики были установлены исходя из наших целей улучшения апи:

        * Ускорение процесса клиентской и серверной̆ разработки;
        * Снижение временных затрат на поддержку и развитие API;
        * Добавление автогенерации документации API;
        * Поддержка версионности API для упрощения поддержки обратной совместимости с помощью версионности API.

        Если вы преследуете другие цели, то возможно у вас будут другие метрики, будет очень интересно, если вы ими поделитесь!

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

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое