Глава 6: Проектирование API
Брайан Кукси - опубликовано 22 апреля 2014
Эта глава знаменует собой поворотный момент в нашем приключении с API. Мы закончили изучение основ и теперь готовы посмотреть, как предыдущие концепции объединяются в API. В этой главе мы обсуждаем компоненты API, разрабатывая его.
Организация данных
По оценкам National Geographic, в 2011 году американцы сделали 80 миллиардов фотографий. Имея такое количество фотографий, вы можете представить себе разные подходы людей к их упорядочению на своих компьютерах. Некоторые предпочитают сбрасывать все в одну папку. Другие тщательно выстраивают свои фотографии в иерархию папок по годам, месяцам и событиям.

Подобным образом и компании относятся к структурированию при создании своих API. Как мы упоминали в главе 1, цель API — облегчить компьютерам работу с данными компании. Имея в виду простоту использования, одна компания может решить создать единый URL-адрес для всех данных и сделать его доступным для поиска (что-то вроде одной папки для всех ваших фотографий). Другая может решить дать каждому фрагменту данных свой собственный URL-адрес, организованный в виде иерархии (как с папками и подпапками для фотографий). Каждая компания выбирает лучший способ структурировать свой API для своей конкретной ситуации, руководствуясь существующими лучшими отраслевыми практиками.
Начните с архитектурного стиля
Обсуждая API, вы можете услышать разговоры о «мыле» (SOAP) и «отдыхе» (REST) и задаться вопросом, работают ли разработчики программного обеспечения или планируют отпуск. На самом деле это названия двух наиболее распространенных архитектур для веб-API:

  • SOAP (изначально бывший сокращением, Simple Object Access Protocol) — это схема взаимодействия на основе XML, которая имеет стандартизованные структуры для запросов и ответов.

  • REST, что означает «передача репрезентативного состояния» (Representational State Transfer), представляет собой более открытый подход, предусматривающий множество соглашений, но оставляющий многие решения на усмотрение человека, разрабатывающего API.

На протяжении этого курса вы, возможно, заметили, что у нас есть склонность к REST API. Это предпочтение во многом связано с невероятной скоростью принятия REST. Это не означает, что SOAP — зло; у него есть свои сильные стороны. Однако в центре нашего обсуждения останется REST, так как это, скорее всего, именно тот API, с которым вы столкнетесь. В остальных разделах мы рассмотрим компоненты, составляющие REST API.
Наш первый ресурс
Еще в главе 2 мы немного поговорили о ресурсах. Напомним, что ресурсы — это существительные в API-интерфейсах (клиенты и пицца). Это то, с чем мы хотим, чтобы мир мог взаимодействовать через наш API.

Чтобы понять, как компания разрабатывает API, давайте попробуем это сделать в нашей пиццерии. Начнем с добавления возможности заказать пиццу.

Чтобы клиент мог поговорить с нами о пицце, нам нужно сделать несколько вещей:

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

Следующим шагом будет присвоение URL-адресов ресурсу. Есть много возможностей, но, к счастью, соглашения REST дают некоторые рекомендации. В типичном REST API ресурсу будут назначены два шаблона URL. Первый — это множественное число от имени ресурса, например /orders . Второй — это множественное число от имени ресурса плюс уникальный идентификатор для указания одного ресурса, например /orders/<order_id>, где <order_id> — уникальный идентификатор для заказа. Эти два шаблона URL составляют первые конечные точки (endpoints), которые будет поддерживать наш API. Они называются конечными точками просто потому, что они идут в конце URL-адреса, как в http://example.com/<endpoint_goes_here>.

Теперь, когда мы выбрали ресурс и присвоили ему URL-адреса, нам нужно решить, какие действия может выполнять клиент. Следуя соглашениям REST, мы говорим, что конечная точка множественного числа (/orders) предназначена для перечисления существующих заказов и создания новых. Множественное число с уникальным идентификатором конечной точки (/orders/<order_id>) предназначено для извлечения, обновления или отмены определенного заказа. Клиент сообщает серверу, какое действие следует выполнить, передавая в запросе соответствующую команду HTTP (GET, POST, PUT или DELETE).

В целом наш API теперь выглядит так:
После того как действия для конечных точек нашего заказа продуманы, последний шаг — решить, какими данными необходимо обмениваться между клиентом и сервером. Заимствуя пример с пиццерией из главы 3, мы можем сказать, что заказ требует корочки и начинки. Нам также необходимо выбрать формат данных, который клиент и сервер могут использовать для передачи этой информации туда и обратно. И XML и JSON — хорошие кандидаты, но для удобства чтения мы выберем JSON.

На этом этапе вы можем себя поздравить; мы разработали работающий API! Вот как может выглядеть взаимодействие между клиентом и сервером с использованием этого API:

Рисунок 1. Пример взаимодействия между клиентом и сервером с использованием нашего API

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

Как и в случае с заказами, нашему ресурсу Клиент нужны конечные точки. Следуя соглашению, /customers и /customers/<id> хорошо подходят. Опустим детали, но скажем, что нужно решить, какие действия имеют смысл для каждой конечной точки и какие данные представляют клиента. Предполагая, что мы все это сделаем, мы приходим к интересному вопросу: как связать заказы с покупателями?

Практики REST разделились во мнениях о том, как решить проблему связывания ресурсов. Некоторые говорят, что иерархия должна продолжать расти, давая конечные точки типа /customers/5/orders для всех заказов клиента №5. И /customers/5/orders/3 для третьего заказа клиента №5. Другие утверждают, что для упрощения ситуации необходимо включать связанные детали в данные для ресурса. Согласно этой парадигме, создание заказа требует отправки поля customer_id с деталями заказа. Оба решения широко используются в REST API, поэтому стоит знать о каждом.

Рисунок 2. Два способа обработки связанных данных при разработке API

Поиск данных
По мере роста объема данных в системе, конечные точки, которые выдают полный перечень записей, становятся непрактичными. Представьте себе, что в нашей пиццерии было три миллиона выполненных заказов, и вы хотели бы узнать, в скольких из них есть пепперони в качестве начинки. Отправка запроса GET на /orders и получение всех трех миллионов заказов не очень помогает. К счастью, в REST есть отличный способ поиска данных.

У URL есть еще один компонент, о котором мы еще не упомянули — строка запроса (query string). Запрос означает поиск, а строка означает текст. Строка запроса — это фрагмент текста, который идет в конец URL-адреса для передачи данных в API. Например, все, что находится после вопросительного знака, является строкой запроса в http://example.com/orders?key=value.

API REST используют строку запроса для указания деталей поиска. Эти сведения называются параметрами запроса. API определяет, какие параметры он будет принимать, и их точные имена должны использоваться, чтобы они могли влиять на поиск. Наш API пиццерии может позволить клиенту искать заказы по топпингам их по следующему URL-адресу: http://example.com/orders?topping=pepperoni . Клиент может включать несколько параметров запроса, перечисляя один за другим, разделяя их амперсандом ("&"). Например: http://example.com/orders?topping=pepperoni&crust=thin

Другое использование строки запроса — ограничение количества данных, возвращаемых в каждом запросе. Часто API-интерфейсы разделяют результаты на наборы (скажем, из 100 или 500 записей) и возвращают по одному набору за раз. Этот процесс разделения данных известен как разбиение на страницы (pagination, аналогия разбиения слов на страницы для книг). Чтобы позволить клиенту просматривать все данные, API будет поддерживать параметры запроса, которые позволяют клиенту указать, какую страницу данных он хочет. В нашем API пиццерии мы можем поддерживать разбиение на страницы, позволяя клиенту указывать два параметра: страницу и размер. Если клиент делает запрос типа GET /orders?page=2&size=200, мы знаем, что ему нужна вторая страница результатов с 200 результатами на страницу, а именно заказы 201-400.
Краткое содержание главы 6
В этой главе мы узнали, как разработать REST API. Мы показали основные функции, поддерживаемые API, и показали, как организовать данные так, чтобы их можно было легко использовать компьютеру.

Ключевые термины, которые мы узнали, были:

  • SOAP: архитектура API, известная стандартизированными форматами сообщений.
  • REST: архитектура API, ориентированная на управление ресурсами.
  • Ресурс: термин API для такого бизнес-объекта, как клиент или заказ.
  • Конечная точка: URL-адрес, составляющий часть API. В REST каждый ресурс получает свои собственные конечные точки.
  • Строка запроса: часть URL-адреса, которая используется для передачи данных на сервер.
  • Параметры запроса: пара "ключ-значение", найденная в строке запроса (топпинг = сыр).
  • Разбиение на страницы: процесс разделения результатов на управляемые части.
Что дальше
В следующей главе мы исследуем способы заставить клиента реагировать на изменения на сервере в режиме реального времени.