Мысли О Веб-Api. Первая Часть

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

статей.

Конечно, мой опыт не абсолютен, и конструктивная критика и дополнения приветствуются.

Чтение получилось скорее философским, чем техническим, но любителям технической части будет над чем задуматься.

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

Я просто попробую сложить всё в единую систему, прежде всего в своей голове, а это уже дорогого стоит. Тем не менее, я буду рад, если мои мысли пригодятся вам в вашей практике.

Итак, начнем.



Приближение первое: Персонажи



Клиент и сервер

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

Точно так же нам не важно, что под капотом, кто встречает запрос у дверей, Apache или Nginx, какой неизвестный зверь, PHP, Python или Ruby его обрабатывает и формирует ответ, какое хранилище данных используется.

: Postgresql, MySQL или MongoDB. Главное, чтобы сервер соблюдал главное правило – услышать, понять и простить.

Клиентом также может быть что угодно, способное генерировать и отправлять HTTP-запрос.

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

Клиентом может быть JavaScript-скрипт, работающий в браузере, мобильное приложение, злой (или не очень) демон, работающий на сервере, или слишком мудрый холодильник (такие уже есть).

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



Философия REST

REST (Передача представительского состояния) изначально задумывался как простой и однозначный интерфейс управления данными, который включал лишь несколько основных операций с непосредственным сетевым хранилищем (сервером): получение данных (ПОЛУЧАТЬ) , сохранение (ПОЧТА) , изменять (ПУСТАТЬ/ИСПРАВИТЬ) и удаление (УДАЛИТЬ) .

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

Кроме того, REST имеет ряд архитектурных принципов, список которых можно найти в любой другой статье о REST. Пробежимся по ним вкратце, чтобы они были под рукой и не пришлось никуда идти: Независимость сервера от клиента — серверы и клиенты могут быть мгновенно заменены другими независимо друг от друга, поскольку интерфейс между ними не меняется.

Сервер не хранит состояния клиентов.

Уникальность адресов ресурсов — каждая единица данных (любой степени вложенности) имеет свой уникальный URL, который, по сути, полностью является уникальным идентификатором ресурса.

Пример: ПОЛУЧИТЕ /api/v1/users/25/имя Независимость формата хранения данных от формата передачи.

– сервер может поддерживать несколько разных форматов передачи одних и тех же данных (JSON, XML и т. д.), но хранит данные в своем внутреннем формате, независимо от того, что поддерживается.

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

навигация.

Позже мы рассмотрим различные типы ресурсов.



Чего нам не хватает?

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

Все это по умолчанию полностью ложится на плечи клиентского приложения.

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

За поддержку этих соединений, т.е.

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

Так чего же нам не хватает в REST?

Вызовы функций

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

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

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

Самый простой пример – авторизация пользователя.

Мы вызываем функцию POST /api/v1/auth/логин , мы передаем ему объект, содержащий в качестве аргумента учетные данные, и в ответ получаем ключ доступа.

Что происходит с данными на стороне сервера нас не волнует. Другой вариант – создание и разрыв связей между сущностями.

Например, добавление пользователя в группу.

Вызов функции группы для сущности POST /api/v1/groups/1/addUser , мы передаем объект пользователя в качестве параметра и получаем результат. Пример, конечно, надуманный, но зачастую при создании соединений возможны дополнительные операции с данными на стороне сервера.

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

).

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



Несколько операций

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

Здесь как минимум несколько вариантов: либо все изменения выполнены, либо они выполнены частично (для некоторых объектов), либо произошла ошибка.

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

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

Попробую сделать это в одном из продолжений.



Статистические запросы, агрегаторы, форматирование данных

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

По сути, это данные, которые генерируются по требованию, более или менее «на лету» и доступны только для чтения, поэтому имеет смысл выделить их в отдельную категорию.

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

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



Типы данных



Объекты

Ключевым типом данных при общении между клиентом и сервером является объект. По сути, объект представляет собой список свойств и соответствующих им значений.

Мы можем отправить объект на сервер в запросе и получить результат запроса как объект. Однако объект не обязательно будет реальным объектом, хранящимся в базе данных, по крайней мере, не в той форме, в которой он был отправлен или получен.

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

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

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

Некоторые свойства объекта могут быть редактируемыми, некоторые являются системными свойствами и доступны только для чтения, а некоторые могут носить статистический характер и рассчитываться на лету (например, количество лайков).

Некоторые свойства объекта могут быть скрыты в зависимости от прав пользователя.



Коллекции объектов

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

добавлять, удалять, изменять объекты и выбирать из них.

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



Скалярные значения

В чистом виде скалярные значения как отдельная сущность встречались на моей памяти крайне редко.

Обычно они появляются как свойства объектов или коллекций, и поэтому их можно читать или записывать.

Например, имя пользователя можно получить и изменить индивидуально.

ПОЛУЧИТЕ /api/v1/users/1/имя .

На практике эта функция редко бывает полезной, но при необходимости хотелось бы иметь ее под рукой.

Это особенно актуально для свойств коллекции, таких как количество записей (с фильтрацией или без): ПОЛУЧИТЬ /api/v1/news/count .



Файлы

Файлы есть файлы — их следует рассматривать как единую неделимую единицу.

Другой вопрос, что в большинстве случаев при сохранении файла в базе данных может быть создана служебная сущность, содержащая метаданные этого файла: размер, настоящее имя, статус и т.д. Продолжение следует. Теги: ##webapi #rest #api #api

Вместе с данным постом часто просматривают: