Pull to refresh
0

Проектирование Web API в 7 шагов

Reading time 14 min
Views 73K
Original author: Mike Amundsen
7steps Разработка веб API это нечто большее чем просто URL, HTTP статус-коды, заголовки и содержимое запроса. Процесс проектирования – то, как будет выглядеть и восприниматься ваш API – очень важен и является хорошей инвестицией в успех вашего дела. Эта статья кратко описывает методологию для проектирования API с опорой на преимущества веба и протокола HTTP, в частности. Но не стоит думать, что это применимо только для HTTP. Если по какой-то причине вам необходимо реализовать работу ваших сервисов используя WebSockets, XMPP, MQTT и так далее – применяя большую часть всех рекомендаций вы получите практически тот же API, который будет хорошо работать. К тому же полученный API позволит легче разработать и поддерживать работу поверх нескольких протоколов.

Хороший дизайн затрагивает URL, статус-коды, заголовки и содержимое запроса


Обычно руководства по проектированию Web API фокусируются на общих концепциях: как проектировать URL, как правильно использовать HTTP статус-коды, методы, что передавать в заголовках и как спроектировать дизайн содержимого, которое представлено сериализованными данными или графом объектов. Это всё очень важные детали реализации, но не настолько в смысле общего проектирования API. Проектирование API – это то, как сама суть сервиса будет описана и представлена, то что вносит значительный вклад в успех и удобность использования Web API.

Хороший процесс проектирования или методология предоставляют набор согласованных и воспроизводимых шагов для создания компонентов сервисов, которые будут доступны в виде Web API. Это значит, что такая прозрачная методология может быть использована разработчиками, дизайнерами и архитекторами для координации своих действий по реализации ПО. Использованная методология так же может уточнятся со временем по мере того, как улучшается и автоматизируется процесс без ущерба для деталей методологии. На самом деле, детали реализации могут меняться (например, платформа, ОС, фреймворки и стиль UI) независимо от процесса проектировки, когда эти две активности полностью разделены и задокументированы.

Проектирование API в 7 шагов.


Далее следует краткий обзор методологии описанной в книге "RESTful Web APIs" за авторством Ричардсона и Амундсена (Richardson and Amundsen). Статья не предполагает детального разбора каждого шага, но постарается дать общее понимание что делается на каждом шаге. Также, читатели могут использовать этот обзор как руководство для разработки процесса проектирования Web API, который учитывает специфику ваших знаний и целей бизнеса.

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

В своём плане вы должны учесть возможную итеративную природу процесса создания сервиса. Например, вы можете проходить через Шаг 2 (Создание диаграмм состояний) и понять, что надо еще кое-чего сделать в Шаге 1 (Перечислить все компоненты). Когда вы будете писать код (Шаг 6), вы можете обнаружить, что пропустили пару моментов в Шаге 5 (Создание семантического профиля), и так далее. Ключевой момент заключается в использовании руководства для обнаружения как можно большего количества деталей и желании возвращаться на шаг-два назад, чтобы описать пропущенные моменты. Итеративность – ключ к построению наиболее полного представления вашего сервиса и видению как он может быть использован клиентскими приложениями.

Шаг 1: Перечислить все компоненты


Первым шагом предлагается перечислить все типы данных, которые клиентское приложение может захотеть получить или передать с помощью сервиса. Мы зовём это семантическим описанием. Семантическое – потому что отображает значение данных в приложении, и описание – потому что содержит описание того, что происходит в самом приложении. Заметьте, что вы должны работать с точки зрения клиентского приложения, а не сервиса. Важно разработать удобный API для использования клиентом.

Например, простое приложения типа «Список дел», вы можете найти следующие семантические описания:
  • id – уникальный идентификатор для каждой записи в системе
  • title – название для каждого «дела»
  • dateDue – дата, к которому «дело» должно быть завершено
  • complete – «да/нет» флажок, который показывает завершено ли «дело»

В настоящем приложении здесь может быть очень много пунктов для отображения таких понятий как категории «дел» (работа, семья, сад и т.д.), пользовательской информации и прочего. Сейчас лучше остановиться на таком простом списке, чтобы можно было сфокусироваться на самом процессе.

Шаг 2: Нарисовать диаграмму состояний


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

Пока не стоит беспокоится на счёт определения какой метод используется для каждого перехода. Просто укажите является ли переход безопасным (HTTP GET), небезопасный/не идемпотентный (HTTP POST) или небезопасный/идемпотентный (PUT)

Заметки на полях: Идемпотентные действия – такие, которые можно повторять без неожиданных сайд-эффектов. Например, HTTP PUT является идемпотентным потому что спецификация говорит, что сервер должен использовать значения состояния, полученные от клиента, для замены любых значений в целевом ресурсе. В то время как HTTP POST не идемпотентен, так как спецификация HTTP указывается что значения, переданные с помощью POST должны быть добавлены к существующим ресурсам, а не замещены.

В нашем случае, клиентское приложение для простейшего «Списка дел» может потребовать доступ к пунктам списка, возможность фильтрации, просматривать отдельные пункты и отмечать их как завершённые. Многие из этих действий используют значения состояний для передачи данных между клиентом и сервером. Например, действие «добавить элемент» позволяет клиенту передавать значения состояний title и dueDate. Ниже диаграмма, которая иллюстрирует основные действия:

7s_1

Действия, показанные на диаграмме и перечисленные, ниже так же являются семантическими дескрипторами – они описывают семантику действий для сервиса.
  • read-list
  • filter-list
  • read-item
  • create-item
  • mark-complete

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

Как только вы пройдёте пару раз через эти два шага у вас будет хорошее понимание всех данных и действий в которых нуждается клиент для взаимодействия с вашим сервисом.

Шаг 3: Согласование магических строк


Следующим шагом будет согласование всех «магических строк» в интерфейсе вашего сервиса. «Магическими строками» в нашем случае будут все названия дескрипторов – они не имеют смысла за пределами задачи, они просто представляют действия или элементы данных к которым клиенты будут обращаться при общении со службой. Согласование имён означает приведение их к широко используемым терминам, используя, например, следующие ресурсы:

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

Заметки на полях: Использование общих имён для дескрипторов может быть хорошей идеей для внешнего интерфейса, однако вас никто не заставляет использовать их для ваших внутренних нужд. Имеются ввиду, например, имена для баз данных. Сервис самостоятельно может использовать карту соответствий между внешними и внутренними именами без каких-либо проблем.

Для To-Do сервиса из примера, у меня получилось найти приемлемые существующие имена для всех дескрипторов кроме одного – «create-item». В этом случае, я обратился к созданию уникального URI на основе правил из Web-Linking RFC 5988. Во время выбора конвенционных имён для интерфейса вас всегда будут преследовать компромиссы. Редко когда удаётся найти идеальное попадание к внутренним именам и это нормально.

Вот что у меня получилось:

Итак, вот как стала выглядеть диаграмма после использования согласования имён:
7s_2

Шаг 4: Выбор типа гипермедиа


Следующим шагом в процессе проектирования вашего API будет выбор типа данных, который будет использоваться для передачи сообщений между сервером и клиентом. Одним из отличительных знаков сети является то, что данные передаются стандартизированными документами через общий интерфейс. Очень важно выбрать такой тип, который поддерживает одинаково дескрипторы данных («identifier», «status») и действий («search», «edit»). Таких форматов достаточно мало.

Вот некоторые из гипермедиа форматов с вершины списка (порядок не имеет значения в этом списке):
  • HyperText Markup Language (HTML)
  • Hypertext Application Language (HAL)
  • Collection+JSON (Cj)
  • Siren
  • JSON-API
  • Uniform Basis for Exchanging Representations (UBER)

На выбор так же должно влиять то, насколько хорошо выбранный формат гипермедиа работает с протоколом передачи данных. Большинство разработчиков предпочитает протокол HTTP для интерфейса сервисов. Однако так же используются и  WebSocketsXMPPMQTT и CoAP – особенно для высоко-скоростных реализаций, с короткими сообщениями, соединениями точка-точка.

Для нашего примера я буду использовать HTML как протокол сообщений и HTTP как протокол связи. HTML уже имеет поддержку необходимых дескрипторов (<UL> для списков, <LI> для элементов, и <SPAN> для данных). Так же в нем есть адекватная поддержка для дескрипторов действий (<A> для безопасных ссылок, <FORM method=«get»> для безопасных переходов и <FORM method=«post»> для небезопасных переходов).

Заметки на полях: Диаграмма состояний сейчас показывает «редактирование» как идемпотентное (HTTP PUT), но HTML до сих пор не имеет встроенной поддержки для PUT. Однако я могу добавить дополнительное поле чтобы эмулировать идемпотентный HTML POST.

Отлично, теперь я могу «опробовать» интерфейс основываясь на состояниях из диаграммы. Для нашего примера, нам надо описать только две вещи: «To-Do List» и «To-Do Item»:
Коллекция «To-Do List» в HTML представлении
<html>
  <head>
    <!-- for test display only -->
    <title>To Do List</title>
    <style>
      .name, .scheduledTime, .status, .item {display:block}
    </style>
  </head>
  <body>
    <!-- for test display only -->
    <h1>To-Do List</h1>
    <!-- to-do list collection -->
    <ul>
      <li>
        <a href="/list/1" rel="item" class="item">
          <span class="identifier">1</span>
        </a>
        <span class="name">First item in the list</span>
        <span class="scheduledTime">2014-12-01</span>
        <span class="status">pending</span>
      </li>
      <li>
        <a href="/list/2" rel="item" class="item">
          <span class="identifier">2</span>
        </a>
        <span class="name">Second item in the list</span>
        <span class="scheduledTime">2014-12-01</span>
        <span class="status">pending</span>
      </li>
      <li>
        <a href="/list/3" rel="item" class="item">
          <span class="identifier">3</span>
        </a>
        <span class="name">Third item in the list</span>
        <span class="scheduledTime">2014-12-01</span>
        <span class="status">complete</span>
      </li>
    </ul>
    <!-- search transition -->
    <form method="get" action="/list/" class="search">
      <legend>Search</legend>
      <input name="name" class="identifier" />
      <input type="submit" value="Name Search" />
    </form>
    <!-- create-item transition -->
    <form method="post" action="/list/" class=">
      <legend>Create Item</legend>
      <input name="name" class="name" />
      <input name="scheduledTime" class="scheduledTime" />
      <input type="submit" value="Create Item" />
    </form>
  </body>
</html>

Коллекция «To-Do Item» в HTML представлении
<html>
  <head>
    <!-- for test display only -->
    <title>To Do List</title>
    <style>
      .name, .scheduledTime, .status, .item, .collection {display:block}
    </style>
  </head>
  <body>
    <!-- for test display only -->
    <h1>To-Do Item</h1>
    <a href="/list/" rel="collection" class="collection">Back to List</a>
    <!-- to-do list collection -->
    <ul>
      <li>
        <a href="/list/1" rel="item" class="item">
          <span class="identifier">1</span>
        </a>
        <span class="name">First item in the list</span>
        <span class="scheduledTime">2014-12-01</span>
        <span class="status">pending</span>
      </li>
    </ul>
    <!-- edit transition -->
    <form method="post" action="/list/1" class="edit">
      <legend>Update Status</legend>
      <input type="hidden" name="etag" value="q1w2e3r4t5y6" class="etag" />
      <input type="text" name="status" value="pending" class="status" />
      <input type="submit" value="Update" />
    </form>
  </body>
</html>

Помните о том, что, работая с примерной реализацией вашей диаграммы состояний, вы можете обнаружить, что что-то пропущено и вам надо вернуться на шаг-два назад. Это нормально и не надо этого пугаться. Сейчас самое время все это опробовать в тестовых примерах – до того, как вы начнёте реализовывать всё в коде.

Когда вы будете довольны тем как всё представлено, остаётся последний шаг перед началом кодирования – создание семантического профиля.

Шаг 5: Создание семантического профиля


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

Следует чётко понимать, что это руководство по реализации, а не инструкция по реализации.

Форматы описания сервиса


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

Основные форматы, которые борются за сердца разработчиков:
  • Web Service Definition Language (WSDL)
  • Atom Service Description (AtomSvc)
  • Web Application Description Language (WADL)
  • Blueprint
  • Swagger
  • RESTful Application Modeling Language (RAML)


Форматы для описания профиля


Существует всего несколько форматов для описания профиля на данный момент. И вот те, которые я рекомендую:

Оба формата достаточно новые. JSON-LD спецификация получила статус W3C Recommendation в начале 2014. Hydra все ещё в состоянии неофициального драфта (на момент написания статьи), но имеет активное сообщество разработчиков. ALPS так же находится на ранней стадии драфта.

Так как идея документа заключается в том, чтобы описать реальные аспекты проблемной области (а не частное решение), формат весьма отличается от типичных описательных форматов:
<html>
<alps version="1.0">
  <doc>
    ALPS profile for InfoQ article on "API Design Methodology"
  </doc>
  <!-- data descriptors -->
  <descriptor id="identifier" type="semantic" ref=" />
  <descriptor id="name" type="semantic" ref=" />
  <descriptor id="scheduledTime" type="semantic" ref=" />
  <descriptor id="status" type="semantic" ref=" />
  <!-- action descriptors -->
  <descriptor id="collection" type="safe" ref=" />
  <descriptor id="item" type="safe" ref=">
    <descriptor href="#identifier" />
  </descriptor>
  <descriptor id="search" type="safe" ref=">
    <descriptor href="#name" />
  </descriptor>
  <descriptor id="create-item" type="unsafe" ref=">
    <descriptor href="#name" />
    <descriptor href="scheduledTime" />
  </descriptor>
  <descriptor id="edit" type="idempotent" ref=">
    <descriptor href="#identifier" />
    <descriptor href="#status" />
  </descriptor>
</alps>

Вы наверно заметили, что документ выглядит как общий словарь всех возможных значений для полей и действий из интерфейса сервиса «Список дел» – и это является сутью идеи. Сервисы, которые соглашаются придерживаться этого профиля, могут принимать собственные решения на счёт протокола, формата сообщений и даже URL. Клиенты, которые соглашаются принимать этот профиль будут созданы таким образом, чтобы понимать и, если возможно, активировать дескрипторы.

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

Но это тема для другой статьи.

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

Шаг 6: Написать немного кода


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

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

После того, как вы написали и стабилизировали ваш код, остаётся последний шаг из списка: Публикация.

Шаг 7: Публикация вашего API


Web API должно публиковать как минимум один URL, который всегда будет отвечать на запросы клиентов – даже в далёком-далёком будущем. Я называю это «billboard URL» –тот, который все знают. Так же будет хорошей идеей публиковать документ профиля для того, чтобы новые реализации сервиса могли ссылаться на него в ответах. Вы так же можете опубликовать по этому адресу читабельную документацию, уроки и прочую информацию, которая может помочь разработчикам понять и использовать ваш сервис.

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

В заключение


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

Ещё раз 7 шагов:
  1. Перечислите все части
    Укажите все типы данных, которые необходимы клиенту для общения с сервисом.
  2. Нарисуйте диаграмму состояний
    Задокументируйте все действия (переходы между состояниями), которые доступны для сервиса
  3. Согласуйте магические переменные
    Приведите имена в вашем публичном интерфейсе к принятым стандартам
  4. Выберите тип гипермедиа
    Выберите формат сообщений, который наиболее полно отображает переходы в сервисе с учётом выбранного протокола.
  5. Создание семантического профиля
    Напишите документ, который будет содержать и определять все дескрипторы, используемые в сервисе
  6. Напишите реализацию
    Распространите семантический профиль и диаграмму состояний среди разработчиков серверной и клиентской частей, чтобы они могли писать код в соответствии с рекомендациями и править профиль/диаграмму по мере необходимости.
  7. Опубликуйте ваш API
    Опубликуйте «billboard URL» и семантический профиль, чтобы другие могли использовать их для создания новых сервисов и/или клиентские приложения.

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

В конце концов, эта методология лишь один из способов для создания надёжного, воспроизводимого, последовательного и цельного процесса по проектированию Web API. В процессе рассмотрения этого примера, вы можете решить, что в вашем случае какие-то шаги надо добавить, какие-то сократить и, конечно, решения по выбору формата обмена данными и протокол могут сильно меняться от проекта к проекту.

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


Статья написана Майком Амундсеном, ведущим архитектором API в Layer 7 Technologies. Он так же известен как автор книг и спикер, консультант путешествующий по США и Европе.

Если вам близка тематика статьи или вы хотите узнать больше о том, как правильно проектировать, поддерживать и работать с API, приходите на нашу конференцию API & Backend!
Если вам есть о чем рассказать, мы ждем ваших историй!
Tags:
Hubs:
+26
Comments 8
Comments Comments 8

Articles

Information

Website
www.gosharp.ru
Registered
Employees
2–10 employees
Location
Россия