Что такое restful. Введение в REST API. Ресурсы и методы

REST API (Representational State Transfer), ​​или веб-службы RESTful, — что это? REST в переводе c английского «репрезентативная передача состояния». Это способ обеспечения взаимодействия между компьютерными системами в Интернете. REST-совместимые веб-службы, позволяющие запрашивающим системам получать доступ к текстовым представлениям веб-ресурсов и управлять ими, используя единый и предопределенный набор операций. Существуют и другие формы веб-служб, которые содержат свои собственные произвольные наборы операций, например WSDL и SOAP.

REST API: что это? Определение понятия

Веб-ресурсы изначально были определены во Всемирной паутине как документы или файлы, идентифицированные их URL-адресами. Сегодня они имеют гораздо более общее и абстрактное определение, охватывающее каждый предмет или сущность, которые могут быть идентифицированы, названы, адресованы или обработаны в Сети. В веб-службе REST API запросы, отравленные в URI-ресурса, вызывают ответ, который может быть оформлен в XML, HTML, JSON или в каком-либо другом формате. Ответ может подтвердить, что некоторые изменения были внесены в хранимый ресурс, также предоставить гипертекстовые ссылки на другие связанные ресурсы и их коллекции. Использование HTTP как наиболее распространенного протокола относится к типам доступных операций, которые предопределены командами PUT, DELETE, HTTP GET, POST.

Используя протоколы без учета состояния и стандартные операции, системы REST нацелены на быструю производительность, надежность и способность к росту путем повторного использования компонентов, которыми можно управлять и которые можно обновлять, не затрагивая систему в целом. Использование REST часто предпочтительнее, чем более тяжелый стиль SOAP (Simple Object Access Protocol), поскольку REST не использует полосы пропускания, что делает его более подходящим для использования в Интернете. Для подхода SOAP требуется запись или использование предоставленной серверной программы (для обслуживания данных) и клиентской программы (для запроса данных).

История технологии

Термин «репрезентативная передача состояния» был введен и определен в 2000 году Роем Филдингом в его диссертации «Архитектурные стили и дизайн сетевых архитектур программного обеспечения». Он выработал архитектурный стиль REST параллельно с HTTP 1.1 1996-1999 годов, основанный на существующем проекте HTTP 1.0 1996 года. В ретроспективном взгляде на развитие технологии Филдинг сказал о том, что на протяжении процесса стандартизации HTTP он был призван защищать выбор дизайна в Интернете. Это очень сложная задача в рамках процесса, принимающего предложения от кого-либо по теме, которая быстро становится центром всей отрасли.

У Филдинга были комментарии от более чем 500 разработчиков, многие из которых отличные инженеры с многолетним опытом. Он должен был объяснить все, начиная с самых абстрактных понятий веб-взаимодействия и заканчивая точными деталями синтаксиса HTTP. Этот процесс оттачивал его модель до основного набора принципов, свойств и ограничений, которые теперь называются REST.

Преимущества

Особенности стиля REST влияют на следующие архитектурные свойства:

  • Производительность - взаимодействие компонентов является доминирующим свойством в восприятии пользователями производительности и результативности сети.
  • Масштабируемость для поддержки максимального количества компонентов, тестирования REST API и взаимодействия между ними.
  • Простота единого интерфейса и авторизации REST API.
  • Модифицируемость компонентов для удовлетворения меняющихся потребностей (даже во время работы приложения).
  • Видимость связи между составляющими и сервисными агентами.
  • Возможность переносить компоненты, перемещая их программный код с данными.
  • Надежность — высокая отказоустойчивость при наличии сбоев в составе, разъемов или данных.

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

Формальные и архитектурные ограничения

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

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

Безопасность

REST не предоставляет встроенной поддержки безопасности. Это очень важно при проектировании веб-сервисов REST - требования безопасности и проектирования выполняются заранее. Веб-сервисы REST используют HTTP PUT и DELETE из CRUD-операций. PUT и DELETE не поддерживаются многими браузерами и чаще всего отключены на уровне сервера из-за возможного нарушения конфиденциальности. Если он не настроен должным образом на уровне сервера и клиента, любой посторонний пользователь сможет создать ресурс с помощью PUT-метода или уничтожить используемый ресурс DELETE. При разработке требований безопасности для веб-сервисов следует учитывать данные моменты.

Архитектурные элементы

Ключевым аспектом REST является характер и состояние его элементов данных. В стиле REST существует четыре понятия, описывающих поведение и состояние информации.

Ресурс — это объект (логический или физический), доступный в Интернете. Это может быть документ, хранящийся в файловой системе сервера или строка в таблице базы данных. Конечный пользователь взаимодействует с ресурсом для достижения определенной цели. Для проектирования системы с помощью REST разработчик должен думать о бизнес-объектах как ресурсах и о том, как они могут быть адресованы.

URI - уникально идентифицирует ресурс. Этот параметр делает ресурс адресным и может быть изменен. Ресурсы изменяются с использованием протокола приложения - такого как HTTP.

Представление - состояния ресурса в момент времени. Клиент получает представление ресурса при запросе URI. Вид ресурса может быть закодирован в одном или нескольких передаваемых форматах, таких как XML, HTML, JSON, RSS, REST API java. Эти форматы могут быть согласованы при помощи механизма согласования контента.

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

Коннектор

Разъем соединителя представляет собой абстрактный интерфейс, который опосредует связь между компонентами. Поскольку взаимодействия REST не имеют состояния, соединитель не должен хранить информацию о состоянии. Следовательно, связь между компонентами может происходить параллельно.

Клиент и сервер являются основными REST-коннекторами. Клиент инициирует запрос, а сервер его обрабатывает.

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

Компоненты

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

User-Agent - использует клиентский соединитель для инициирования запроса.

Origin-сервер - использует серверный коннектор для ответа на запрос.

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

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

Перспективы развития

Всегда актуален вопрос: REST API — что это для современных интернет-технологий? REST - основа современной веб-архитектуры, которая развивается путем анализа недостатков уже существующих стилей и введения новых дополнений к ней.

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

Зачем, например, заморачиваться с методом DELETE или там заголовком Accept? Не проще ли использовать метод GET и передавать все в параметрах, например, delete=true или format=json ? Вбил в браузере, и работает! А вот этот ваш DELETE так просто через браузер не пошлешь. На что я ответил примерно так.

Вот, допустим, у вас есть некоторые ресурсы. Для определенности, пусть это будут книги и пользователи. Что, собственно, означает иметь REST API для работы с этими ресурсами? В первом приближении, следующее. Если мы хотим получить какую-то книгу, то говорим GET /books/123 . Аналогично информация о пользователе получается запросом GET /users/456 . Вообще-то, в начале URL неплохо бы иметь что-то вроде /api/v1.0/ , но для краткости мы это опустим. По умолчанию данные отдаются, например, в JSON’е , но при желании мы можем передать Accept-заголовок с другим форматом. Для создания или обновления существующей книги следует использовать метод PUT, передав данные в теле запроса и указав формат этих данных в заголовке Content-type. Для удаления данных используется метод DELETE.

Внимательный читатель спросит, а для чего тогда нужен POST? Вообще, если делать все по науке, он должен использоваться для добавления элементов в сущность, словно она является неким контейнером, например, словарем. Однако на практике так обычно не делают, ведь при использовании API несколькими клиентами один клиент может изменить название книги, а второй — ее цену, в результате чего получится ерунда. Поэтому POST либо вообще не используют, либо используют в качестве замены методов PUT и DELETE. То есть, POST с каким-то телом запроса работает, как PUT, а без тела запроса — как DELETE. Это позволяет работать с клиентами, которые почему-то не умеют посылать PUT и DELETE.

Можно работать и сразу с целыми коллекциями. Для получения списка всех пользователей говорим GET /users , а для создания нового пользователя с автоматически сгенерированным id — POST /users . Как и ранее, в последнем случае данные передаются в теле запроса. Также можно перезаписать всю коллекцию, сказав PUT /users , и удалить сразу всех пользователей, сказав DELETE /users . Еще иногда требуется фильтрация по полям или пагинация, в этих случаях делают так:

GET /api/v1.0/users?fields=id,email,url&offset=100&limit=10&order_by=id

… или как-то так:

GET /api/v1.0/logs?from=2013-01-01+00:00:00&to=2013-12-31+23:59:59

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

В свое время я имел удовольствие работать над проектом, где API был устроен «простым и понятным» образом, на методах GET и POST, со всякими delete=1 и так далее. Смею вас заверить, что на самом деле вы этого не хотите. Потому что на практике работа с этим API превращается в какой-то кошмар.

Допустим, один программист занимается книгами, а второй пользователями. Первый решает, что для получения списка всех сущностей будет использоваться запрос GET /all_books , а второй решает перечислять только id и использовать URL GET /select_user_ids . Для удаления сущности первый программист решает использовать параметр del=true , а второй — delete=1 . Для экспорта данных в CSV первый программист делает поддержку export=text/csv , а второй — format=CSV . Потом выясняется, что некоторые библиотеки не умеют посылать GET-запросы со слишком длинными query string и ходить за данными на чтение начинают методом POST. А затем кто-то случайно удаляет через браузер всех пользователей в боевом окружении… И так далее, и тому подобное, полный бардак в общем.

Вы спросите, что же мешает привести все это безобразие в одному стандарту, например, использовать только del=1 и export=csv ? Так вот, REST — это и есть то самое приведение к одному стандарту , с учетом всяческих граблей типа случайного удаления данных через браузер и так далее. Притом у разных компаний этот стандарт одинаковый. Когда в команду разработчиков приходит новичок, вы просто говорите ему, что у вас всюду REST, а основные ресурсы — это пользователи и книги. Все, после этого одного предложения ваш новый коллега знает 90% API, безо всякого там чтения Wiki. Если вы хотите говорить с иностранцами, вы же просто используете общепринятый английский язык , а не изобретаете новый? Вот так же и здесь. Нельзя также не напомнить о пользе повторного использования протоколов и кода. А ведь для работы с REST, и HTTP вообще, написана куча библиотек и фреймворков.

Вы скажите «я, конечно, согласен, что REST такой весь из себя интуитивно понятный и общепринятый, но что, если я просто хочу загрузить через браузер список книг в формате CSV»? Тут важно понимать, что REST — это не о том, как сделать все через браузер . Предполагается, что должен быть клиент, который умеет работать с вашим API, вот через него и экспортируете. Но если по каким-то причинам это затруднительно, вы можете, например, использовать curl. Если у вас нелады с консолью, вы без труда найдете множество GUI-клиентов или, скажем, какой-нибудь плагин для Chrome, с аналогичным функционалом. Однако я все же советую попробовать curl. Пользоваться им совсем не так сложно, как вам может казаться. Всего-то нужно запомнить десяток параметров.

Так задаются дополнительные HTTP-заголовки:

H "Accept: text/csv" -H "Content-type: application/json"

Выбираем используемый метод:

X{GET|PUT|POST|DELETE}

Указываем тело запроса:

D "{"name":"Alex","url":"http://сайт/"}"

D @filename.json
# чтобы при этом не удалялись символы новой строки:
--data-binary @filename.json

Выводим заголовки из ответа сервера в stdout:

Говорим передавать данные в gzip’е:

Сохраняем тело ответа в указанный файл вместо stdout:

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

Теперь рассмотрим пару примеров.

Экспорт книг в формате CSV:

curl -H "Accept: text/csv" http://localhost/api/v1.0/books -o books.csv

Создание пользователя c выводом заголовков из ответа сервера в stdout:

curl -XPOST -H "Content-type: application/json" -d "{"name":"Alex"}" \
http://localhost/api/v1.0/users -D -

Удаление пользователя с заданным id:

curl -XDELETE http://localhost/api/v1.0/users/123

Несложно, правда ведь?

Несколько финальных замечаний, относящихся не совсем к REST. Во-первых, иногда от приложения требуется не только предоставлять доступ к некоторым ресурсам, но и выполнять какие-то команды. Таким командам имеет смысл выделять URL-адреса, начинающиеся с /commands/ . Например, запуск почтовой рассылки по всем пользователям будет выглядеть как-то так:

curl -XPOST -H "Content-type: application/json" \
-d "{"subject":"Good news, everyone!","body":"..."}" \
http://localhost/api/v1.0/commands/notify_all_users_via_email

Дополнение: Некоторые команды должны быть доступны только в тестовом окружении, для них можно выделить URL-адреса, начинающиеся с /debug/ .

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

curl -H "Accept: application/x-json-stream" \
http://localhost/api/v1.0/streams/users -N

{"type":"user","data":{"id":123,"name":"Alex","url":"http://сайт/"}}
{"type":"user","data":{"id":456,"name":"Bob","url":"http://ya.ru/"}}
...
{"type":"sync"}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"user_deleted","data":{"id":123}}
...

Нужно обратить внимание на несколько моментов. Здесь используется формат x-json-stream , то есть, поток JSON-объектов, разделенных символом \n. Если этот символ встречается в самом JSON-объекте, его, соответственно, следует кодировать. Некоторым клиентам может быть удобнее работать с честным JSON’ом, то есть, списком JSON-объектов. Предусмотреть поддержку сразу нескольких форматов довольно просто. Во втором случае список объектов должен начинаться с открывающейся квадратной скобки, а объекты должны разделяться запятыми. Для удобства работы со стримом нужно либо ставить после запятых символ \n, либо делать это на стороне клиента с помощью sed:

curl ... | sed "s/},/}\n/g"

Каждый объект имеет поле type и опциональное поле data. Объекты с типом heartbeat посылаются несмотря ни на что один раз в пять секунд. Если клиент не видит такого объекта в течение десяти секунд, он считает, что либо что-то сломалось на стороне сервера, либо что-то не так с сетью, и закрывает соединение. Объект с типом sync используется в стримах, посылающих некое состояние, а затем обновления к нему, для разделения первого от второго. Наконец, все остальные типы представляют собой полезную нагрузку. Поле data нужно по той причине, что вложенные данные также могут иметь поле type, что приводило бы к неразберихе.

В-третьих, когда вы пишите RESTful приложение, старайтесь с самого начала придерживаться некоторых соглашений. Например, с самого начала договоритесь, что имена полей в JSON-объектах должны всегда писаться в camelCase. Раз и навсегда запретите использовать в идентификаторах такие спецсимволы, как знак плюс и пробелы. Договоритесь, что в случае получения кода 301 клиент должен посылать точно такой же запрос на URL, указанный в заголовке Location. Примите соглашение о том, как будет передаваться автоматически сгенерированные id. Например, в Riak для этого используется заголовок Location . Подумайте о том, как вы будете сообщать о различных типах ошибок, в том числе временной недоступности БД, ошибках валидации полей и так далее. Пользователи почти наверняка предпочтут увидеть:

{"message":"validation_error","description":"..."}

… вместо кода 500 без каких-либо дополнительных пояснений. Если для вашего приложения важна точность представления чисел, договоритесь передавать все числа в виде строк, чтобы json-декодер не терял точность из-за преобразования строк во float’ы.

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

В данной статье я расскажу Вам о том, что такое REST API . Также мы затронем тему HTTP протокола. А также рассмотрим пример архитектурного дизайна REST API.

Немного теории

О том, что такое API, я подробно рассказывал . Напомню, что API – это некий набор правил, с помощью которых приложение или какой-либо один его компонент могут взаимодействовать, общаться, если хотите, с другим приложением или компонентом. Прикладной интерфейс программирования (API ) может возвращать данные в разных форматах, например в JSON , XML или в бинарном формате, но в REST API мы будем использовать JSON -формат, как наиболее удобный.

Давайте посмотрим на пример. Возможно, Вы уже знакомы с тем, что такое система контроля версий Git . Ее web-версия – это Github . Так вот, у Github есть собственное API , с помощью которого можно получить какую-либо полезную информацию, например о пользователях и организациях, их проектах, и т.д. Давайте взглянем на пример:

Curl https://api.github.com/orgs/Yandex

В этом примере мы используем консольную утилиту curl для того, чтобы получить данные через API . Ее можно загрузить с официального сайт проекта . Она позволяет делать все то же самое что и расширение curl в PHP , только для этого не нужно писать код, так как вся функциональность доступна посредством интерфейса командной строки. Вообще, незаменимая вещь для тестирования различных прикладных интерфейсов . Есть еще альтернатива в виде расширения для Chrome – Postman .

Данная команда вернет нам большой JSON-объект , содержащий различные данные о компании.

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

Если говорить еще проще то, REST – это архитектурный стиль, а RESTful API – это его практическое воплощение, и чем больше приложение отвечает критериям стиля REST , тем более оно RESTful .

RESTful API сводится к четырем базовым операциям:

  • получение данных в удобном для клиента формате
  • создание новых данных
  • обновление данных
  • удаление данных

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

  • GET – получение
  • POST – создание
  • PUT – обновление, модификация
  • DELETE – удаление

Все эти методы в совокупности называют CRUD (create, read, update, delete) – (создать, прочитать, обновить, удалить) операциями.

Фактически в REST существует единственный, непротиворечивый общий интерфейс для запросов, например, к базам данных, что является его важнейшим преимуществом. На следующей картинке показано соответствие HTTP методов SQL операциям и концепции CRUD .

Т.е. HTTP метод POST соответствует SQL операции INSERT, метод GET – операции SELECT и т.д .

Для каждого HTTP запроса есть свой статус. И они нужны, чтобы грамотно с точки зрения REST API оформить ответ и отдать клиенту. Статусов много, поэтому их всех не перечислить, однако важно знать их группировку:

  • 100 – 199 – это статусы несущие информационный характер
  • 200 - 299 – статусы успешной операции
  • 300 – 399 – статусы перенаправления (редиректа)
  • 400 – 499 – статусы ошибок на стороне клиента
  • 500 – 599 – статусы ошибок на стороне сервера

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

  • http://site.com/api.php?action=create.user&id=1&key=46syhy77sash
  • http://site.com/api.php?action=delete.user&id=1&key=46syhy77sash
  • http://site.com/api.php?action=get.user&id=1&key=46syhy77sash

где параметр

  • action – это действие, которое необходимо выполнить
  • id – идентификатор пользователя
  • кey – ключ доступа (фактически, временный пароль)

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

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

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

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

Часть 1. Теория

Итак, как мы все знаем, API - application programming interface (интерфейс программирования приложений), набор правил и механизмов, с помощью которых одно приложение или компонент взаимодействует с другими

Почему хороший API - это важно?

  • Простота использования и поддержки . Хороший API просто использовать и поддерживать.
  • Хорошая конверсия в среде разработчиков . Если всем нравится ваш API, к вам приходят новые клиенты и пользователи.
  • Выше популярность вашего сервиса . Чем больше пользователей API, тем выше популярность вашего сервиса.
  • Лучше изоляция компонентов . Чем лучше структура API, тем лучше изоляция компонентов.
  • Хорошее впечатление о продукте . API - это как бы UI разработчиков; это то, на что разработчики обращают внимание в первую очередь при встрече с продуктом. Если API кривой, вы как технический эксперт не будете рекомендовать компаниям использовать такой продукт, приобретая что-то стороннее.

Теперь посмотрим, какие бывают виды API.

Виды API по способу реализации:

  • Web service APIs
    • XML-RPC and JSON-RPC
  • WebSockets APIs
  • Library-based APIs
    • Java Script
  • Class-based APIs
    • C# API
  • OS function and routines
    • Access to file system
    • Access to user interface
  • Object remoting APIs
    • CORBA
    • .Net remoting
  • Hardware APIs
    • Video acceleration (OpenCL…)
    • Hard disk drives
    • PCI bus


Как мы видим, к Web API относятся XML-RPC и JSON-RPC, SOAP и REST.

RPC (remote procedure call - «удаленный вызов процедур») - понятие очень старое, объединяющие древние, средние и современные протоколы, которые позволяют вызвать метод в другом приложении. XML-RPC - протокол, появившийся в 1998 г. вскоре после появления XML. Изначально он поддерживался Microsoft, но вскоре Microsoft полностью переключилась на SOAP, поэтому в.Net Framework мы не найдем классов для поддержки этого протокола. Несмотря на это, XML-RPC продолжает жить до сих пор в различных языках (особенно в PHP) - видимо, заслужил любовь разработчиков простотой.

SOAP также появился в 1998 г. стараниями Microsoft. Он был анонсирован как революция в мире ПО. Нельзя сказать, что все пошло по плану Microsoft: было огромное количество критики из-за сложности и тяжеловесности протокола. В то же время, были и те, кто считал SOAP настоящим прорывом. Протокол продолжал развиваться и плодиться десятками новых и новых спецификаций, пока в 2003 г. W3C не утвердила в качестве рекомендации SOAP 1.2, который и сейчас - последний. Семейство у SOAP получилось внушительное: WS-Addressing, WS-Enumeration, WS-Eventing, WS-Transfer, WS-Trust, WS-Federation, Web Single Sign-On.

Затем, что закономерно, все же появился действительно простой подход - REST. Аббревиатура REST расшифровывается как representational state transfer - «передача состояния представления» или, лучше сказать, представление данных в удобном для клиента формате. Термин “REST” был введен Роем Филдингом в 2000 г. Основная идея REST в том, что каждое обращение к сервису переводит клиентское приложение в новое состояние. По сути, REST - не протокол и не стандарт, а подход, архитектурный стиль проектирования API.

Каковы принципы REST?

  • Клиент-серверная архитектура - без этого REST немыслим.
  • Любые данные - ресурс .
  • Любой ресурс имеет ID , по которому можно получить данные.
  • Ресурсы могут быть связаны между собой - для этого в составе ответа передается либо ID, либо, как чаще рекомендуется, ссылка. Но я пока не дошел до того, чтобы все было настолько хорошо, чтобы можно было легко использовать ссылки.
  • Используются стандартные методы HTTP (GET, POST, PUT, DELETE) - т. к. они уже заложены в составе протокола, мы их можем использовать для того, чтобы построить каркас взаимодействия с нашим сервером.
  • Сервер не хранит состояние - это значит, сервер не отделяет один вызов от другого, не сохраняет все сессии в памяти. Если у вас есть какое-либо масштабируемое облако, какая-то ферма из серверов, которая реализует ваш сервис, нет необходимости обеспечивать согласованность состояния этих сервисов между всеми узлами, которые у вас есть. Это сильно упрощает масштабирование - при добавлении еще одного узла все прекрасно работает.

Чем REST хорош?

  • Он очень прост!
  • Мы переиспользуем существующие стандарты , которые в ходу уже очень давно и применяются на многих устройствах.
  • REST основывается на HTTP => доступны все плюшки:
    • Кэширование.
    • Масштабирование.
    • Минимум накладных расходов.
    • Стандартные коды ошибок.
  • Очень хорошая распространенность (даже IoT-устройства уже умеют работать на HTTP).
Лучшие решения (независимые от технологий)
Какие в современном мире есть лучшие решения, не связанные с конкретной реализацией? Эти решения советую использовать обязательно:
  • SSL повсюду - самое важное в вашем сервисе, т. к. без SSL авторизация и аутентификация бессмысленны.
  • Документация и версионность сервиса - с первого дня работы.
  • Методы POST и PUT должны возвращать обратно объект, который они изменили или создали, - это позволит сократить время обращения к сервису вдвое.
  • Поддержка фильтрации, сортировки и постраничного вывода - очень желательно, чтобы это было стандартно и работало «из коробки».
  • Поддержка MediaType . MediaType - способ сказать серверу, в каком формате вы хотите получить содержимое. Если вы возьмете какую-либо стандартную реализацию web API и зайдете туда из браузера, API отдаст вам XML, а если зайдете через какой-нибудь Postman, он вернет JSON.
  • Prettyprint & gzip . Не минимизируйте запросы и не делайте компакт для JSON (того ответа, который придет от сервера). Накладные расходы на prettyprint -единицы процентов, что видно, если посмотреть, сколько занимают табы по отношению к общему размеру сообщения. Если вы уберете табы и будете присылать все в одну строку, запаритесь с отладкой. Что касается gzip, он дает выигрыш в разы. Т. ч. очень советую использовать и prettyprint, и gzip.
  • Используйте только стандартный механизм кэширования (ETag) и Last-Modified (дата последнего изменения) - этих двух параметров серверу достаточно, чтобы клиент понял, что содержимое не требует обновления. Придумывать что-то свое тут не имеет смысла.
  • Всегда используйте стандартные коды ошибок HTTP . Иначе вам однажды придется кому-нибудь объяснять, почему вы решили, что ошибку 419 в вашем проекте клиенту нужно трактовать именно так, как вы почему-то придумали. Это неудобно и некрасиво - за это клиент вам спасибо не скажет!
Свойства HTTP-методов

Сегодня мы будем говорить только про GET, POST, PUT, DELETE.

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

Как вы видите, все методы, кроме POST, представленные в таблице, идемпотентны. Идемпотентность - возможность выполнить одно и то же обращение к сервису несколько раз, при этом ответ каждый раз будет одинаковым. Другими словами, не важно, по какой причине и сколько раз вы выполнили это действие. Допустим, вы выполняли действие по изменению объекта (PUT), и вам пришла ошибка. Вы не знаете, что ее вызвало и в какой момент, вы не знаете, изменился объект или нет. Но, благодаря идемпотентности, вы гарантированно можете выполнить этой действие еще раз, т. ч. клиенты могут быть спокойны за целостность своих данных.

“Safe” же значит, что обращение к серверу не изменяет содержимое. Так, GET может быть вызван много раз, но он не изменит никакого содержимого. Если бы он изменял содержимое, в силу того, что GET может быть закэширован, вам пришлось бы бороться с кэшированием, изобретать какие-нибудь хитрые параметры.

Часть 2. Практика
Выбираем технологию

Теперь, когда мы поняли, как работает REST, можем приступить к написанию RESTful API ¬ сервиса, отвечающего принципам REST. Начнем с выбора технологии.

Первый вариант - WCF Services . Все, кто работал с этой технологией, обычно возвращаться к ней больше не хотят - у нее есть серьезные недостатки и мало плюсов:
– webHttpBinding only (а зачем тогда остальные?..).
– Поддерживаются только HTTP Get & POST (и все).
+ Разные форматы XML, JSON, ATOM.

Второй вариант - Web API . В этом случае плюсы очевидны:
+ Очень простой.
+ Открытый исходный код.
+ Все возможности HTTP.
+ Все возможности MVC.
+ Легкий.
+ Тоже поддерживает кучу форматов.

Естественно, мы выбираем Web API. Теперь выберем подходящий хостинг для Web API.

Выбираем хостинг для Web API

Тут есть достаточно вариантов:

  • ASP.NET MVC (старый добрый).
  • Azure (облачная структура).
  • OWIN - Open Web Interface for .NET (свежая разработка от Microsoft).
  • Self-hosted
OWI
OWIN - не платформа и не библиотека, а спецификация, которая устраняет сильную связанность веб-приложения с реализацией сервера. Она позволяет запускать приложения на любой платформе, поддерживающей OWIN, без изменений. На самом деле, спецификация очень проста - это просто «словарь» из параметров и их значений. Базовые параметры определены в спецификации.

OWIN сводится к очень простой конструкции:

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

Katana - реализация OWIN от Microsoft. Она позволяет размещать OWIN-сборки в IIS. Вот так она выглядит, очень просто:

Namespace RestApiDemo { public class Startup { public void Configuration(IAppBuilder app) { var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); app.UseWebApi(config); } } }

Вы указываете, какой класс является у вас Startup. Это простой dll, который поднимается IIS. Вызывается конфигуратор. Этого кода достаточно, чтобы все заработало.

Проектируем интерфейс
Теперь спроектируем интерфейс и посмотрим, как все должно выглядеть и каким правилам соответствовать. Все ресурсы в REST - существительные, то, что можно пощупать и потрогать.

Как пример возьмем простую модель с расписанием движения поездов на станциях. Вот примеры простейших запросов REST:

  • Корневые (независимые) сущности API:
    • GET /stations - получить все вокзалы.
    • GET /stations/123 - получить информацию по вокзалу с ID = 123.
    • GET /trains - расписание всех поездов.
  • Зависимые (от корневой) сущности:
    • GET /stations/555/departures - поезда, уходящие с вокзала 555.
Контроллер

Итак, у нас есть станции, и теперь нам нужно написать простейший контроллер:

Public class RailwayStationsController: ApiController { public IEnumerable GetAll() { return testData; } RailwayStationModel testData = /*initialization here*/ }

Это роутинг, построенный на атрибутах. Здесь мы указываем имя контроллера и просим отдать список (в данном случае - случайные тестовые данные).

OData (www.odata.org)

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

Public class RailwayStationsController: ApiController { public IQueryable GetAll() { return testData.AsQueryable(); } RailwayStationModel testData = /*initialization here*/ }

IQueryable позволяет вам использовать несколько простых, но эффективных механизмов фильтрации и управления данными на клиентской стороне. Единственное, что нужно сделать, - подключить OData-сборку из NuGet, указать EnableQuery и возвращать интерфейс iQueryable.

Основное отличие такой облегченной верси от полноценной в том, что здесь нет контроллера, который возвращает метаданные. Полноценная OData немного изменяет ответ (заворачивает в спец. Обертку модель, которую вы собираетесь возвращать) и умеет возвращать связанное дерево объектов, которые вы хотите ей отдать. Также облегченная версия OData не умеет делать штуки вроде join, count и т. д.

Параметры запросов

А вот что можно делать:

  • $filter - фильтр, по имени, например. Все функции можно посмотреть на сайте OData - они очень помогают и позволяют существенно ограничить выборку.
  • $select - очень важная штука. Если у вас большая коллекция и все объекты толстые, но при этом вам нужно сформировать какой-то dropdown, в котором нет ничего, кроме ID и имени, которое вы хотите отобразить, - поможет эта функция, которая упростит и ускорит взаимодействие с сервером.
  • $orderby - сортировка.
  • $top и $skip - ограничение по выборкам.

Этого достаточно, чтобы самому не изобретать велосипеда. Все это умеет стандартная JS-библиотека вроде Breeze.

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

Именно для таких случаев у атрибута EnableQuery (см. код выше) есть такой набор параметров, с помощью которых очень многое можно ограничить: не давать больше строк, чем надо, не давать делать join, арифметические операции и т. д. При этом писать самому ничего не надо.

  • AllowedArithmeticOperators
  • AllowedFunctions
  • AllowedLogicalOperators
  • AllowedOrderByProperties
  • AllowedQueryOptions
  • EnableConstantParameterization
  • EnsureStableOrdering
  • HandleNullPropagation
  • MaxAnyAllExpressionDepth
  • MaxExpansionDepth
  • MaxNodeCount
  • MaxOrderByNodeCount
  • MaxSkip
  • MaxTop
  • PageSize

Зависимый контроллер
Итак, вот примеры простейших запросов REST:

  • GET /stations – получить все вокзалы
  • GET /trains – расписание всех поездов
  • GET /stations/555/arrivals
  • GET /stations/555/departures

Допустим, у нас есть вокзал 555, и мы хотим получить все его отправления и прибытия. Очевидно, что здесь должна использоваться сущность, которая зависит от сущности вокзала. Но как это сделать в контроллерах? Если мы все это будет делать роутинг-атрибутами и складывать в один класс, понятно, что в таком примере, как у нас, проблем нет. Но если у вас будет десяток вложенных сущностей и глубина будет расти еще дальше, все это вырастет в неподдерживаемый формат.

И тут есть простое решение - в роутинг-атрибутах в контроллерах можно делать переменные:

Public class TrainsFromController: TrainsController { public IQueryable GetAll(int station) { return GetAllTrips().Where(x =>

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

Единственное, возникает проблема - здесь у нас “stations”, и до этого был “stations”. Если вы в одном месте что-то поменяете, а в другом - ничего не поменяете, ничего работать не будет. Однако тут есть простое решение - использование констант для роутинга :

Public static class TrainsFromControllerRoutes { public const string BasePrefix = RailwayStationsControllerRoutes.BasePrefix + "/{station:int}/departures"; public const string GetById = "{id:int}"; }

Тогда зависимый контроллер будет выглядеть так:

Public class TrainsFromController: TrainsController { public IQueryable GetAll(int station) { return GetAll().Where(x => x.OriginRailwayStationId == station); } }

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

CRUD
Итак, мы с вами обсудили, как могут выглядеть простейшие GET-операции. Все понимают, как сделать единичный GET. Но, кроме него, нам нужно обсудить еще три операции.
  • POST – создать новую сущность
    • POST /Stations – JSON-описание сущности целиком. Действие добавляет новую сущность в коллекцию.
    • Возвращает созданную сущность (во-первых, чтобы не было двойных походов к серверу, во-вторых, чтобы, если это нужно, вернуть со стороны сервера параметры, которые посчитались в этом объекте и нужны вам на клиенте).
  • PUT - изменить сущность
    • PUT /Stations/12 - Изменить сущность с ID = 12. JSON, который придет в параметре, будет записан поверх.
    • Возвращает измененную сущность. Путь, который был применен много раз, должен приводить систему к одному и тому же состоянию.
  • DELETE
    • DELETE /Stations/12 - удалить сущность с ID = 12.

Еще примеры CRUD:

  • POST /Stations - добавляем вокзал.
  • POST /Stations/1/Departures - добавляем информацию об отправлении с вокзала 1.
  • DELETE /Stations/1/Departures/14 - удаляем запись об отправлении с вокзала 1.
  • GET /Stations/33/Departures/10/Tickets - список проданных билетов для отправления 10 с вокзала 33.

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

Антишаблоны
А вот примеры, как делать не надо:
  • GET /Stations/?op=departure&train=11
    Здесь query string используется не только для передачи данных, но и для действий.
  • GET /Stations/DeleteAll
    Это реальный пример из жизни. Тут мы делаем GET на этот адрес, и он, по идее, должен удалить все сущности из коллекции - в итоге он ведет себя очень непредсказуемо из-за кэширования.
  • POST /GetUserActivity
    На самом деле здесь GET, который записан как POST. POST нужен был из-за параметров запроса в body, но в body у GET нельзя ничего передать - GET можно передать только в query string. GET даже по стандарту не поддерживает body.
  • POST /Stations/Create
    Здесь действие указано в составе URL - это избыточно.
Проектируем API
Допустим, у вас есть API, который вы хотите предложить людям, и есть доменная модель. Как связаны сущности API с доменной моделью? Да никак они не связаны, на самом деле. В этом нет никакой необходимости: то, что вы делаете в API, никак не связано с вашей внутренней доменной моделью.

Может возникнуть вопрос, как проектировать API, если это не CRUD? Для этого мы записываем любые действия как команды на изменения. Мы делаем сохранение, чтение, удаление команды, GET, проверку статуса этой команды. GET из коллекции команд - вы получаете список всех команд, которые вы отправляли для какой-либо конкретной сущности.

Доменная модель
Мы поговорим о связи доменной модели с объектами. В примере у нас есть отель (Hotel), есть бронирования (Reservation), комнаты (Room) и устройства (Device), к ним привязанные. В нашем проекте это позволяло управлять комнатами посредством этих устройств.

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

Bounded context (BC)
Bounded context (изолированный поддомен) - фактически, наборы объектов, не зависимые друг от друга и имеющие совершенно независимые модели (разные). В примере мы можем взять и растащить отели и устройства на два разных BC - они не связаны между собой, но присутствует дублирование. Возникает дополнительная сущность (AttachedDevice):

Тут у нас разные представления одного и того же устройства, и в этом нет ничего страшного.

В DDD aggregate route - сущность, которая владеет всеми потомками. Это вершина нашего дерева (Hotel); то, за что можно вытянуть все остальное. А AttachedDevice так взять нельзя - его не существует, и он не имеет никакого смысла. Так же и классы Room и Reservation не имеют никакого смысла, будучи оторванными от Hotel. Поэтому доступ ко всем этим классам - исключительно через рутовую сущность, через Hotel, в данном случае. Device же - другой route с самого начала, другое дерево с другим набором полей.

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

А вот примеры запросов , как они могут выглядеть в такой доменной модели:

  • PUT /hotels/555/rooms/105/attachedDevices - заменить всю коллекцию привязанных устройств на новую.
  • POST /hotels/555/rooms/105/attachedDevices - привязать еще одно устройство.
  • DELETE /hotels/12 - удалить описание отеля с ID=12.
  • POST /hotels/123/reservations - создать новую резервацию в отеле ID=123.
CQRS - Command Query Responsibility Segregation

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

У нас есть один поток, через который пользователь отправляет на сервер команду об изменении домена. Однако не факт, что изменение действительно произойдет, - пользователь не оперирует данными непосредственно. Итак, после того как пользователь посылает команду на изменение сущности, сервер ее обрабатывает и перекладывает в какую-то модель, которая оптимизирована на чтение - UI считывает это.

Такой подход позволит вам следовать принципам REST очень легко. Если есть команда, значит, есть сущность «список команд».

REST without PUT
В простом CRUD-мире PUT - это штука, которая меняет объекты. Но если мы строго следуем принципу CQRS и делаем все через команды, PUT у нас пропадает, т. к. мы не можем менять объекты. Вместо этого можем лишь послать объекту команду на изменение. При этом можно отслеживать статус выполнения, отменять команды (DELETE), легко хранить историю изменений, а пользователь ничего не изменяет, а просто сообщает о намерениях.

Парадигма REST without PUT - пока еще спорная и не до конца проверенная, но для каких-то случаев действительно хорошо применима.

Fine-grained VS coarse-grained
Представьте, что вы делаете большой сервис, большой объект. Тут у вас есть два подхода: fine-grained API и coarse-grained API («мелкозернистый» и «крупнозернистый» API).

Fine-grained API:

  • Много маленьких объектов.
  • Бизнес-логика уходит на сторону клиента.
  • Нужно знать, как связаны объекты.

Сoarse-grained API:

  • Создаете больше сущностей.
  • Сложно делать локальные изменения, например
    • POST /blogs/{id}/likes.
  • Нужно отслеживать состояние на клиенте.
  • Большие объекты нельзя сохранить частично.

Для начала советую проектировать fine-grained API: каждый раз, когда вы создаете объект, отправляете его на сервер. На каждое действие на стороне клиента происходит обращение к серверу. Однако с маленькими сущностями работать проще, чем с большими: если вы напишете большую сущность, вам трудно будет потом ее распилить, трудно будет делать небольшие изменения и выдергивать из нее независимые куски. Т. ч. лучше начинать с маленьких сущностей и постепенно их укрупнять.

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

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

Какие известны на текущий момент варианты нумерации версий Web API?

Самое простое - указать версию в URL.

Вот готовые варианты, когда самому ничего делать не надо:

Вот один интересный готовый вариант.

Это всего лишь роутинг атрибутов с constraint - если вы делали какие-либо серьезные объекты, наверняка делали constraint. По номеру версии в этом атрибуте ребята просто реализовали constraint. Соответственно, на один и тот же атрибут с разными версиями, но одинаковым именем контроллера вешаете на два разных класса и указываете разные версии. Все работает «из коробки….

VersionedRoute("v2/values", Version = 2)]
config.ConfigureVersioning(
versioningHeaderName: "version", vesioningMediaTypes: null);
config.ConfigureVersioning(
versioningHeaderName: null,
vesioningMediaTypes: new { "application/vnd.model"});

Документация
Есть чудесная open-source-штука, имеющая множество различных применений - Swagger. Мы ее используем со специальным адаптером - Swashbuckle.
  • http://swagger.io/
  • https://github.com/domaindrivendev/Swashbuckle
Swashbuckle: httpConfiguration .EnableSwagger(c => c.SingleApiVersion("v1", ”Demo API")) .EnableSwaggerUi(); public static void RegisterSwagger(this HttpConfiguration config) { config.EnableSwagger(c => { c.SingleApiVersion("v1", "DotNextRZD.PublicAPI") .Description("DotNextRZD Public API") .TermsOfService("Terms and conditions") .Contact(cc => cc .Name("Vyacheslav Mikhaylov") .Url("http://www.dotnextrzd.com") .Email("[email protected]")) .License(lc => lc.Name("License").Url("http://tempuri.org/license")); c.IncludeXmlComme nts(GetXmlCommentFile()); c.GroupActionsBy(GetControllerGroupingKey); c.OrderActionGroupsBy(new CustomActionNameComparer()); c.CustomProvider(p => new CustomSwaggerProvider(config, p)); }) .EnableSwaggerUi(c => { c.InjectStylesheet(Assembly.GetExecutingAssembly(), "DotNextRZD.PublicApi.Swagger.Styles.SwaggerCustom.css"); }); } }

Теги: Добавить метки

Некоторые скажут:

«Это API , который использует HTTP-запросы для

GET, PUT, POST и DELETE ".

Многие также скажут:

«Речь идет об определении ресурсов с помощью URI»

Другие будут кричать:

В заключение можно сказать:

«В принципе, это просто API , который использует HTTP правильно!»

Некоторые из этих утверждений ошибочны,

Некоторые частично верны,

но это не имеет значения,

Потому что они все упускают суть .

Чтобы иметь возможность разрабатывать API RESTful,

Во-первых,

Что такое REST?

Что такое REST?

REST это:

    Определенно не HTTP

    Не протокол

    Не спецификация

Вот почему вокруг REST APIs так много споров.

И всё-таки...

REST это стиль архитектуры.

О, хорошо… но что такое стиль архитектуры?

Стиль архитектуры

Желаемая архитектура

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

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

REST выступает за представление состояния передачи

Это было записано Роем Филдингом в его докторской диссертации в 2000 году, где он описал существующую и современную веб-архитектуру как абстракцию.

Название было призвано вызывать представление о том, как ведет себя хорошо разработанное веб-приложение.

Рой описал REST на простом примере:

Рассмотрим сеть веб-страниц как виртуальную машину состояний.

Каждая страница представляет состояние:

1. Во-первых, пользователь получает первое состояние в виде индексного состояния.

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

3. Результат передачи следующего состояния пользователю.

REST все еще не HTTP

Конечно, еще в 2000 году сеть уже работала на HTTP, и Рой со своими коллегами-инженерами много работали над этим.

Однако REST не определяет конкретные детали реализации системы и не определяет какой-либо синтаксис протокола.

Вполне возможно иметь архитектуру RESTful поверх протоколов, отличных от HTTP.

Например, CoAP (протокол ограниченного применения) является протоколом RESTful для встроенных устройств (Internet of Things) и предназначен для использования минимальных ресурсов как на устройстве, так и в сети.

Так зачем использовать REST?

Всемирная паутина основана на архитектуре REST.

Поэтому, если вы создаете API-интерфейс non-RESTful, который будет использоваться в Интернете, то вы получите неоптимальную систему.Не оптимальный в отношении оптимизированной архитектуры.

Это важно отметить, поскольку не-RESTful API может быть неоптимальным в сетевой архитектуре, но оптимальным для других проблем. Например, современные интерфейсные приложения могут иметь очень специфические потребности, следовательно, растет число библиотек сбора данных, таких как GraphQL или Falcor.

Итак, когда это API RESTful?

API является RESTful, когда он постоянно действует под ограничениями REST.

REST определяет 6 ограничений для достижения желаемой оптимизации системы:

1. Клиент-сервер

Это ограничение основано на принципе разделения интересов.

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

2. Без гражданства

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

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

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

Когда мы создаем наш API, он не должен игнорировать кеширование.

4. Правильный интерфейс

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

4.1. Идентификация ресурсов

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

4.2. Манипулирование ресурсами через представления

Ресурс может быть представлен различными способами.

Например, HTML, XML, JSON или даже JPEG-файл.

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

4.3 В-третьих, самоописательные сообщения

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

4.4. Hypermedia должна быть двигателем состояния приложения

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

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

5. Многоуровневая система

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

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

6. Код по требованию

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

Итак, как создать REST API?

Правильно использовать HTTP

Если вы строите RESTful API, используйте протокол RESTful. Для Интернета HTTP-протокол является определенным выбором. Создайте свой API, чтобы он правильно использовал HTTP.

Создайте единый интерфейс

Сопоставьте свои концепции с ресурсами и назначьте соответствующие идентификаторы для каждого из них. Простым примером может служить служба базы данных пользователей. В такой службе мы можем назвать два ресурса; Пользователей и пользователей (ресурс сбора). Эти ресурсы могут быть идентифицированы с URI / users и / user / {id} URI вашего интерфейса API.

Управляйте своим API-гиперссылками

Помните об архитектуре REST

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

Если у вас появились какие-либо вопросы, приглашаем на наши

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

Поделиться